Process Hacker
growl.c
Go to the documentation of this file.
1 #define _CRT_RAND_S
2 #include <phdk.h>
3 #include <winsock2.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <time.h>
8 
9 #include "md5.h"
10 #include "tcp.h"
11 #include "growl.h"
12 
13 static const char hex_table[] = "0123456789ABCDEF";
14 static char* string_to_hex_alloc(const char* str, int len) {
15  int n, l;
16  char* tmp = (char*)PhAllocateSafe(len * 2 + 1);
17  if (tmp) {
18  memset(tmp, 0, len * 2 + 1);
19  for (l = 0, n = 0; l < len; l++) {
20  tmp[n++] = hex_table[(str[l] & 0xF0) >> 4];
21  tmp[n++] = hex_table[str[l] & 0x0F];
22  }
23  }
24  return tmp;
25 }
26 
27 int growl_init_ = 0;
28 
29 /* dmex: modified to use latest Winsock version */
31 {
32  if (growl_init_ == 0)
33  {
34 #ifdef _WIN32
35  WSADATA wsaData;
36  if (WSAStartup(WINSOCK_VERSION, &wsaData) != 0)
37  {
38  return -1;
39  }
40 #endif
41 
42  growl_init_ = 1;
43  }
44  return 1;
45 }
46 
47 
49 {
50  if (growl_init_ == 1)
51  {
52 #ifdef _WIN32
53  WSACleanup();
54 #endif
55  }
56 }
57 
58 /* dmex: modified to use a version of rand with security enhancements */
59 char* gen_salt_alloc(int count)
60 {
61  char* salt = (char*)PhAllocateSafe(count + 1);
62 
63  if (salt)
64  {
65  int n;
66  int randSeed = 0;
67 
68  rand_s(&randSeed);
69 
70  for (n = 0; n < count; n++)
71  salt[n] = (randSeed % 255) + 1;
72 
73  salt[n] = 0;
74  }
75 
76  return salt;
77 }
78 
79 char* gen_password_hash_alloc(const char* password, const char* salt) {
80  md5_context md5ctx;
81  char md5tmp[20];
82  char* md5digest;
83 
84  memset(md5tmp, 0, sizeof(md5tmp));
85  md5_starts(&md5ctx);
86  md5_update(&md5ctx, (uint8_t*)password, (uint32_t)strlen(password));
87  md5_update(&md5ctx, (uint8_t*)salt, (uint32_t)strlen(salt));
88  md5_finish(&md5ctx, (uint8_t*)md5tmp);
89 
90  md5_starts(&md5ctx);
91  md5_update(&md5ctx, (uint8_t*)md5tmp, 16);
92  md5_finish(&md5ctx, (uint8_t*)md5tmp);
93  md5digest = string_to_hex_alloc(md5tmp, 16);
94 
95  return md5digest;
96 }
97 
98 char *growl_generate_authheader_alloc(const char*const password)
99 {
100  char* salt;
101  char* salthash;
102  char* keyhash;
103  char* authheader = NULL;
104 
105  if (password) {
106  salt = gen_salt_alloc(8);
107  if (salt) {
108  keyhash = gen_password_hash_alloc(password, salt);
109  if (keyhash) {
110  salthash = string_to_hex_alloc(salt, 8);
111  if (salthash) {
112  authheader = (char*)PhAllocateSafe(strlen(keyhash) + strlen(salthash) + 7);
113  if (authheader) {
114  sprintf(authheader, " MD5:%s.%s", keyhash, salthash);
115  }
116  PhFree(salthash);
117  }
118  PhFree(keyhash);
119  }
120  PhFree(salt);
121  }
122  }
123 
124  return authheader;
125 }
126 
127 /* dmex: modified to use INVALID_SOCKET */
128 int growl_tcp_register( const char *const server , const char *const appname , const char **const notifications , const int notifications_count ,
129  const char *const password, const char* const icon )
130 {
131  SOCKET sock = INVALID_SOCKET;
132  int i=0;
133  char *authheader;
134  char *iconid = NULL;
135  FILE *iconfile = NULL;
136  size_t iconsize;
137  uint8_t buffer[1024];
138 
139  growl_init();
140  authheader = growl_generate_authheader_alloc(password);
141  sock = growl_tcp_open(server);
142  if (sock == INVALID_SOCKET) goto leave;
143  if (icon) {
144  size_t bytes_read;
145  md5_context md5ctx;
146  char md5tmp[20];
147  iconfile = fopen(icon, "rb");
148  if (iconfile) {
149  fseek(iconfile, 0, SEEK_END);
150  iconsize = ftell(iconfile);
151  fseek(iconfile, 0, SEEK_SET);
152  memset(md5tmp, 0, sizeof(md5tmp));
153  md5_starts(&md5ctx);
154  while (!feof(iconfile)) {
155  bytes_read = fread(buffer, 1, 1024, iconfile);
156  if (bytes_read) md5_update(&md5ctx, buffer, (uint32_t)bytes_read);
157  }
158  fseek(iconfile, 0, SEEK_SET);
159  md5_finish(&md5ctx, md5tmp);
160  iconid = string_to_hex_alloc(md5tmp, 16);
161  }
162  }
163 
164  growl_tcp_write(sock, "GNTP/1.0 REGISTER NONE %s", authheader ? authheader : "");
165  growl_tcp_write(sock, "Application-Name: %s", appname);
166  if(iconid)
167  {
168  growl_tcp_write(sock, "Application-Icon: x-growl-resource://%s", iconid);
169  }
170  else if(icon)
171  {
172  growl_tcp_write(sock, "Application-Icon: %s", icon );
173  }
174  growl_tcp_write(sock, "Notifications-Count: %d", notifications_count);
175  growl_tcp_write(sock, "" );
176 
177  for(i=0;i<notifications_count;i++)
178  {
179  growl_tcp_write(sock, "Notification-Name: %s", notifications[i]);
180  growl_tcp_write(sock, "Notification-Display-Name: %s", notifications[i]);
181  growl_tcp_write(sock, "Notification-Enabled: True" );
182  if(iconid)
183  {
184  growl_tcp_write(sock, "Notification-Icon: x-growl-resource://%s", iconid);
185  }
186  else if(icon)
187  {
188  growl_tcp_write(sock, "Notification-Icon: %s", icon );
189  }
190  growl_tcp_write(sock, "" );
191  }
192 
193  if (iconid)
194  {
195  growl_tcp_write(sock, "Identifier: %s", iconid);
196  growl_tcp_write(sock, "Length: %d", iconsize);
197  growl_tcp_write(sock, "" );
198 
199  while (!feof(iconfile))
200  {
201  size_t bytes_read = fread(buffer, 1, 1024, iconfile);
202  if (bytes_read) growl_tcp_write_raw(sock, buffer, (int)bytes_read);
203  }
204  growl_tcp_write(sock, "" );
205 
206  }
207  growl_tcp_write(sock, "" );
208 
209  while (1)
210  {
211  char* line = growl_tcp_read(sock);
212  if (!line) {
213  growl_tcp_close(sock);
214  sock = INVALID_SOCKET;
215  goto leave;
216  }
217  else
218  {
219  int len = (int)strlen(line);
220  /* fprintf(stderr, "%s\n", line); */
221  if (strncmp(line, "GNTP/1.0 -ERROR", 15) == 0)
222  {
223  if (strncmp(line + 15, " NONE", 5) != 0)
224  {
225  fprintf(stderr, "failed to register notification\n");
226  PhFree(line);
227  goto leave;
228  }
229  }
230  PhFree(line);
231  if (len == 0) break;
232  }
233  }
234  growl_tcp_close(sock);
235  sock = 0;
236 
237  leave:
238  if (iconfile) fclose(iconfile);
239  if (iconid) PhFree(iconid);
240  if (authheader) PhFree(authheader);
241 
242  return (sock == 0) ? 0 : INVALID_SOCKET;
243 }
244 
245 /* dmex: modified to use INVALID_SOCKET */
246 int growl_tcp_notify( const char *const server,const char *const appname,const char *const notify,const char *const title, const char *const message ,
247  const char *const password, const char* const url, const char* const icon)
248 {
249  SOCKET sock = INVALID_SOCKET;
250 
251  char *authheader = growl_generate_authheader_alloc(password);
252  char *iconid = NULL;
253  FILE *iconfile = NULL;
254  size_t iconsize;
255  uint8_t buffer[1024];
256 
257  growl_init();
258 
259  sock = growl_tcp_open(server);
260  if (sock == INVALID_SOCKET) goto leave;
261 
262  if (icon)
263  {
264  size_t bytes_read;
265  md5_context md5ctx;
266  char md5tmp[20];
267  iconfile = fopen(icon, "rb");
268  if (iconfile)
269  {
270  fseek(iconfile, 0, SEEK_END);
271  iconsize = ftell(iconfile);
272  fseek(iconfile, 0, SEEK_SET);
273  memset(md5tmp, 0, sizeof(md5tmp));
274  md5_starts(&md5ctx);
275  while (!feof(iconfile))
276  {
277  bytes_read = fread(buffer, 1, 1024, iconfile);
278  if (bytes_read) md5_update(&md5ctx, buffer, (uint32_t)bytes_read);
279  }
280  fseek(iconfile, 0, SEEK_SET);
281  md5_finish(&md5ctx, md5tmp);
282  iconid = string_to_hex_alloc(md5tmp, 16);
283  }
284  }
285 
286  growl_tcp_write(sock, "GNTP/1.0 NOTIFY NONE %s", authheader ? authheader : "");
287  growl_tcp_write(sock, "Application-Name: %s", appname);
288  growl_tcp_write(sock, "Notification-Name: %s", notify);
289  growl_tcp_write(sock, "Notification-Title: %s", title);
290  growl_tcp_write(sock, "Notification-Text: %s", message);
291  if(iconid)
292  {
293  growl_tcp_write(sock, "Notification-Icon: x-growl-resource://%s", iconid);
294  }
295  else if(icon)
296  {
297  growl_tcp_write(sock, "Notification-Icon: %s", icon );
298  }
299  if (url) growl_tcp_write(sock, "Notification-Callback-Target: %s", url );
300 
301  if (iconid)
302  {
303  growl_tcp_write(sock, "Identifier: %s", iconid);
304  growl_tcp_write(sock, "Length: %d", iconsize);
305  growl_tcp_write(sock, "");
306  while (!feof(iconfile))
307  {
308  size_t bytes_read = fread(buffer, 1, 1024, iconfile);
309  if (bytes_read) growl_tcp_write_raw(sock, buffer, (int)bytes_read);
310  }
311  growl_tcp_write(sock, "" );
312  }
313  growl_tcp_write(sock, "");
314 
315 
316  while (1)
317  {
318  char* line = growl_tcp_read(sock);
319  if (!line)
320  {
321  growl_tcp_close(sock);
322  sock = INVALID_SOCKET;
323  goto leave;
324  } else
325  {
326  int len = (int)strlen(line);
327  /* fprintf(stderr, "%s\n", line); */
328  if (strncmp(line, "GNTP/1.0 -ERROR", 15) == 0)
329  {
330  if (strncmp(line + 15, " NONE", 5) != 0)
331  {
332  fprintf(stderr, "failed to post notification\n");
333  PhFree(line);
334  goto leave;
335  }
336  }
337  PhFree(line);
338  if (len == 0) break;
339  }
340  }
341  growl_tcp_close(sock);
342  sock = 0;
343 
344 leave:
345  if (iconfile) fclose(iconfile);
346  if (iconid) PhFree(iconid);
347  if (authheader) PhFree(authheader);
348 
349  return (sock == 0) ? 0 : INVALID_SOCKET;
350 }
351 
352 
353 
354 int growl( const char *const server,const char *const appname,const char *const notify,const char *const title, const char *const message ,
355  const char *const icon , const char *const password , const char *url )
356 {
357  int rc = growl_tcp_register( server , appname , (const char **const)&notify , 1 , password, icon );
358  if( rc == 0 )
359  {
360  rc = growl_tcp_notify( server, appname, notify, title, message , password, url, icon );
361  }
362  return rc;
363 }
364 
365 
366 void growl_append_md5( unsigned char *const data , const int data_length , const char *const password )
367 {
368  md5_context md5ctx;
369  char md5tmp[20];
370 
371  memset(md5tmp, 0, sizeof(md5tmp));
372  md5_starts(&md5ctx);
373  md5_update(&md5ctx, (uint8_t*)data, data_length );
374  if(password != NULL)
375  {
376  md5_update(&md5ctx, (uint8_t*)password, (uint32_t)strlen(password));
377  }
378  md5_finish(&md5ctx, (uint8_t*)md5tmp);
379 
380  memcpy( data + data_length , md5tmp , 16 );
381 }
382 
383 
384 int growl_udp_register( const char *const server , const char *const appname , const char **const notifications , const int notifications_count , const char *const password )
385 {
386  int register_header_length = 22+(int)strlen(appname);
387  unsigned char *data;
388  int pointer = 0;
389  int rc = 0;
390  int i=0;
391 
392  uint8_t GROWL_PROTOCOL_VERSION = 1;
393  uint8_t GROWL_TYPE_REGISTRATION = 0;
394 
395  uint16_t appname_length = ntohs((u_short)strlen(appname));
396  uint8_t _notifications_count = notifications_count;
397  uint8_t default_notifications_count = notifications_count;
398  uint8_t j;
399 
400  growl_init();
401 
402  for(i=0;i<notifications_count;i++)
403  {
404  register_header_length += 3 + (int)strlen(notifications[i]);
405  }
406  data = (unsigned char*)PhAllocateSafe(register_header_length);
407  if (!data) return -1;
408  memset( data , 0 , register_header_length );
409 
410 
411  pointer = 0;
412  memcpy( data + pointer , &GROWL_PROTOCOL_VERSION , 1 );
413  pointer++;
414  memcpy( data + pointer , &GROWL_TYPE_REGISTRATION , 1 );
415  pointer++;
416  memcpy( data + pointer , &appname_length , 2 );
417  pointer += 2;
418  memcpy( data + pointer , &_notifications_count , 1 );
419  pointer++;
420  memcpy( data + pointer, &default_notifications_count , 1 );
421  pointer++;
422  sprintf( (char*)data + pointer , "%s" , appname );
423  pointer += (int)strlen(appname);
424 
425  for(i=0;i<notifications_count;i++)
426  {
427  uint16_t notify_length = ntohs((u_short)strlen(notifications[i]));
428  memcpy( data + pointer, &notify_length , 2 );
429  pointer +=2;
430  sprintf( (char*)data + pointer , "%s" , notifications[i] );
431  pointer += (int)strlen(notifications[i]);
432  }
433 
434  for(j=0;j<notifications_count;j++)
435  {
436  memcpy( data + pointer , &j , 1 );
437  pointer++;
438  }
439 
440  growl_append_md5( data , pointer , password );
441  pointer += 16;
442 
443  rc = growl_tcp_datagram( server , data , pointer );
444  PhFree(data);
445  return rc;
446 }
447 
448 
449 int growl_udp_notify( const char *const server,const char *const appname,const char *const notify,const char *const title, const char *const message ,
450  const char *const password )
451 {
452  int notify_header_length = 28 + (int)(strlen(appname)+strlen(notify)+strlen(message)+strlen(title));
453  unsigned char *data = (unsigned char*)PhAllocateSafe(notify_header_length);
454  int pointer = 0;
455  int rc = 0;
456 
457  uint8_t GROWL_PROTOCOL_VERSION = 1;
458  uint8_t GROWL_TYPE_NOTIFICATION = 1;
459 
460  uint16_t flags = ntohs(0);
461  uint16_t appname_length = ntohs((u_short)strlen(appname));
462  uint16_t notify_length = ntohs((u_short)strlen(notify));
463  uint16_t title_length = ntohs((u_short)strlen(title));
464  uint16_t message_length = ntohs((u_short)strlen(message));
465 
466  if (!data) return -1;
467 
468  growl_init();
469  memset( data , 0 , notify_header_length );
470 
471  pointer = 0;
472  memcpy( data + pointer , &GROWL_PROTOCOL_VERSION , 1 );
473  pointer++;
474  memcpy( data + pointer , &GROWL_TYPE_NOTIFICATION , 1 );
475  pointer++;
476  memcpy( data + pointer , &flags , 2 );
477  pointer += 2;
478  memcpy( data + pointer , &notify_length , 2 );
479  pointer += 2;
480  memcpy( data + pointer , &title_length , 2 );
481  pointer += 2;
482  memcpy( data + pointer , &message_length , 2 );
483  pointer += 2;
484  memcpy( data + pointer , &appname_length , 2 );
485  pointer += 2;
486  strcpy( (char*)data + pointer , notify );
487  pointer += (int)strlen(notify);
488  strcpy( (char*)data + pointer , title );
489  pointer += (int)strlen(title);
490  strcpy( (char*)data + pointer , message );
491  pointer += (int)strlen(message);
492  strcpy( (char*)data + pointer , appname );
493  pointer += (int)strlen(appname);
494 
495 
496  growl_append_md5( data , pointer , password );
497  pointer += 16;
498 
499 
500  rc = growl_tcp_datagram( server , data , pointer );
501  PhFree(data);
502  return rc;
503 }
504 
505 
506 int growl_udp( const char *const server,const char *const appname,const char *const notify,const char *const title, const char *const message ,
507  const char *const icon , const char *const password , const char *url )
508 {
509  int rc = growl_udp_register( server , appname , (const char **const)&notify , 1 , password );
510  if( rc == 0 )
511  {
512  rc = growl_udp_notify( server, appname, notify, title, message , password );
513  }
514  return rc;
515 }
516 
518 "[The \"BSD licence\"]\r\n"
519 "Copyright (c) 2009-2010 Yasuhiro Matsumoto\r\n"
520 "All rights reserved.\r\n"
521 "\r\n"
522 "Redistribution and use in source and binary forms, with or without\r\n"
523 "modification, are permitted provided that the following conditions\r\n"
524 "are met:\r\n"
525 "\r\n"
526 " 1. Redistributions of source code must retain the above copyright\r\n"
527 " notice, this list of conditions and the following disclaimer.\r\n"
528 " 2. Redistributions in binary form must reproduce the above copyright\r\n"
529 " notice, this list of conditions and the following disclaimer in the\r\n"
530 " documentation and/or other materials provided with the distribution.\r\n"
531 " 3. The name of the author may not be used to endorse or promote products\r\n"
532 " derived from this software without specific prior written permission.\r\n"
533 "\r\n"
534 "THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\r\n"
535 "IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\r\n"
536 "OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\r\n"
537 "IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\r\n"
538 "INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\r\n"
539 "NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\r\n"
540 "DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\r\n"
541 "THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r\n"
542 "(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF\r\n"
543 "THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r\n"
544 ;