1 /****************************************************************************/
2 /* wmckgmail 1.1 : A dockapp to monitor the number of unread mails in a */
4 /* Author : Sylvain Tremblay <stremblay@gmail.com> */
5 /* Release date : Sep 09, 2006 */
6 /****************************************************************************/
8 /* -------------------- */
10 /* -------------------- */
13 #define COUNTERWINDOW 0
16 /* -------------------- */
18 /* -------------------- */
23 #include <sys/types.h>
31 #include "../wmgeneral/wmgeneral.h"
32 #include "../wmgeneral/misc.h"
34 #include "wmckgmail.xpm"
36 /* -------------------- */
37 /* Functions prototypes */
38 /* -------------------- */
39 void writeString(char* sString
, int iWhichWindow
);
40 void writeChar(char cTheChar
, int iWhichWindow
, int iPos
);
41 void writeNum(char cTheNum
, int iWhichWindow
, int iPos
);
42 void trace(char *sMsg
);
43 int getNewMailsCount(char *sAtomFilePath
);
44 void getAtomFile(void);
45 void initialize(void);
47 /* -------------------- */
48 /* Global variables */
49 /* -------------------- */
50 char wmckgmail_mask_bits
[64*64]; /* matrix for the XPM */
51 int wmckgmail_mask_width
= 64;
52 int wmckgmail_mask_height
= 64;
53 char sWorkDir
[1024]; /* to store the directory where wmckgmail works */
54 char sHomeDir
[1024]; /* to store the home directory of the user */
55 char sAtomFile
[1024]; /* to store the path to the atom file */
56 char sConfigFile
[1024];/* to store the path of the configuration file */
57 char sBrowserCmd1
[1024];/* to store the command line #1 to launch the browser */
58 char sBrowserCmd2
[1024];/* to store the command line #2 to launch the browser */
59 char sUname
[20]; /* to store the gmail username */
60 char sPass
[20]; /* to store the gmail password */
61 int iPollInterval
; /* to store the poll interval (in seconds) */
62 time_t lastPoll
; /* to store the time of the last incoming mails verification */
67 static char *progname
;
69 /* -------------------- */
71 /* -------------------- */
72 int main(int argc
, char **argv
){
76 char sMsg
[1024]; /* used to create tracing messages */
80 createXBMfromXPM(wmckgmail_mask_bits
, wmckgmail
, wmckgmail_mask_width
, wmckgmail_mask_height
);
81 openXwindow(argc
, argv
, wmckgmail
, wmckgmail_mask_bits
, wmckgmail_mask_width
, wmckgmail_mask_height
);
83 /* Define the mouse regions */
84 AddMouseRegion(BIG_M_
, 16, 9, 46, 31);
88 /* Read config file and initialize variables */
91 /* Do a first verification out of the main loop */
93 getNewMailsCount(sAtomFile
);
100 /* Update every iPollInterval seconds */
101 if((time(NULL
) - lastPoll
) >= (time_t) iPollInterval
){
103 getNewMailsCount(sAtomFile
);
105 if(DEBUG
) system("date\necho \"-----\"");
108 while(XPending(display
)){
109 XNextEvent(display
, &Event
);
116 XCloseDisplay(display
);
120 i
= CheckMouseRegion(Event
.xbutton
.x
, Event
.xbutton
.y
);
122 sprintf(sMsg
, "button pressed! region index is: %i\n", but_stat
); trace(sMsg
);
126 i
= CheckMouseRegion(Event
.xbutton
.x
, Event
.xbutton
.y
);
127 if(but_stat
== i
&& but_stat
>= 0){
129 sprintf(sMsg
, "button released! region index is: %i\n", i
); trace(sMsg
);
133 /* action when the big _M_ is pressed */
134 /*system("mozilla -remote \"openURL(http://www.gmail.com)\"");*/
135 trace("\nClicked the big M ! Verifying if there is an action to do...\n");
136 if(strcmp(sBrowserCmd1
, "")){
137 trace("'browsercmd1' has been defined, trying the command...\n");
138 if(system(sBrowserCmd1
)){
139 trace("Command failed.\n");
140 if(strcmp(sBrowserCmd2
, "")){
141 trace("'browsercmd2 has been defined, trying the command...\n");
142 system(sBrowserCmd2
);
145 trace("Command succed (returned 0)\n");
148 trace("No browsercmd1 nor browsercmd2 defined, nothing to do.\n");
154 /* action for region 1 */
166 /* ----------------------------------------------------------------- */
167 /* Function : writeString */
168 /* Usage : Write a string in a specific window */
170 /* Arguments: sString -> The string to write */
171 /* iWhichWindow -> The window to write to */
173 /* Returns : Nothing */
174 /* ----------------------------------------------------------------- */
175 void writeString(char* sString
, int iWhichWindow
){
178 /* loop to process each character of the string and call the right */
179 /* function according if the character is alphabetical or numerical */
180 for(i
= 0; i
< strlen(sString
); i
++){
181 if(sString
[i
] >= '0' && sString
[i
] <= '9'){
182 writeNum(sString
[i
], iWhichWindow
, i
);
184 writeChar(sString
[i
], iWhichWindow
, i
);
189 /* ----------------------------------------------------------------- */
190 /* Function : writeChar */
191 /* Usage : Write a character in a specific window at a specific */
194 /* Arguments: cTheChar -> Character to write */
195 /* iWhichWindow -> The window to write to */
196 /* iPos -> The position of the character */
198 /* Returns : Nothing */
199 /* ----------------------------------------------------------------- */
200 void writeChar(char cTheChar
, int iWhichWindow
, int iPos
){
201 int iLetterIdx
; /* The alphebetical index of the letter (A=0, B=1, ...) */
202 int iSrcX
; /* The X coordinate in the XPM of the letter to copy */
203 int iSrcY
; /* The Y coordinate in the XPM of the letter to copy */
204 int iDestX
; /* The X coordinate in the XPM where we want to copy the letter */
205 int iDestY
; /* The Y coordinate in the XPM where we want to copy the letter */
206 int iWidth
; /* The width of each letter in the XPM */
207 int iHeight
; /* The height of each letter in the XPM */
208 int iMaxPos
; /* The maximum value for the position of the letter to write */
210 /* Set the variables according to the window in which we want to print a letter */
211 if(iWhichWindow
== COUNTERWINDOW
){
223 /* If trying to write pass the iMaxPos position, exit the function */
228 /* Clear the actual position in case of something is already there */
229 copyXPMArea((iSrcX
+ (26 * iWidth
)), iSrcY
, iWidth
, iHeight
, (iDestX
+ (iPos
* iWidth
)), iDestY
);
231 /* Get the index of the letter we want to print */
232 iLetterIdx
= (toupper(cTheChar
) - 'A');
234 /* If the index is NOT between 0 and 25, exit the function */
235 /* Using an invalid character is also the way to "erase" a letter */
236 if(iLetterIdx
< 0 || iLetterIdx
> 25){
240 /* Let's write this letter at the right place! */
241 copyXPMArea((iSrcX
+ (iLetterIdx
* iWidth
)), iSrcY
, iWidth
, iHeight
, (iDestX
+ (iPos
* iWidth
)), iDestY
);
245 /* ----------------------------------------------------------------- */
246 /* Function : writeNum */
247 /* Usage : Write a number in a specific window at a specific */
250 /* Arguments: cTheNum -> Number to write */
251 /* iWhichWindow -> The window to write to */
252 /* iPos -> The position of the number */
254 /* Returns : Nothing */
255 /* ----------------------------------------------------------------- */
256 void writeNum(char cTheNum
, int iWhichWindow
, int iPos
){
257 int iNumberIdx
; /* The numerical index of the number */
258 int iSrcX
; /* The X coordinate in the XPM of the number to copy */
259 int iSrcY
; /* The Y coordinate in the XPM of the number to copy */
260 int iDestX
; /* The X coordinate in the XPM where we want to copy the number */
261 int iDestY
; /* The Y coordinate in the XPM where we want to copy the number */
262 int iWidth
; /* The width of each number in the XPM */
263 int iHeight
; /* The height of each number in the XPM */
264 int iMaxPos
; /* The maximum value for the position of the number to write */
266 /* Set the variables according to the window in which we want to print a number */
267 if(iWhichWindow
== COUNTERWINDOW
){
279 /* If trying to write pass the iMaxPos position, exit the function */
284 /* Clear the actual position in case of something is already there */
285 copyXPMArea((iSrcX
+ (10 * iWidth
)), iSrcY
, iWidth
, iHeight
, (iDestX
+ (iPos
* iWidth
)), iDestY
);
287 /* Get the index of the number we want to print */
288 iNumberIdx
= (cTheNum
- '0');
290 /* If the index is NOT between 0 and 9, exit the function */
291 /* Using an invalid character is also a way to "erase" a number */
292 if(iNumberIdx
< 0 || iNumberIdx
> 9){
296 /* Let's write this letter at the right place! */
297 copyXPMArea((iSrcX
+ (iNumberIdx
* iWidth
)), iSrcY
, iWidth
, iHeight
, (iDestX
+ (iPos
* iWidth
)), iDestY
);
300 /* ----------------------------------------------------------------- */
301 /* Function : trace */
302 /* Usage : Output traces to stdout if the DEBUG #define is not */
305 /* Arguments: sMsg -> The message to output */
307 /* Returns : Nothing */
308 /* ----------------------------------------------------------------- */
309 void trace(char *sMsg
){
315 /* ----------------------------------------------------------------- */
316 /* Function : getNewMailsCount */
317 /* Usage : Read the number of new mails from the atom file */
319 /* Arguments: sAtomFilePath -> Path to the atom file */
321 /* Returns : Number of new mails */
322 /* ----------------------------------------------------------------- */
323 int getNewMailsCount(char *sAtomFilePath
){
324 FILE* fAtomFile
; /* The file pointer to the atom file */
325 int fdAtomFile
; /* The file descriptor of the atom file */
326 int iNewMails
= 0; /* Return the number of new mails through this var */
327 char sNewMails
[5]; /* To contain the string version of the number of new mails */
328 int iLength
; /* used for the length of the strinc containing the new mails count */
329 struct stat statBuffer
; /* Struct to contain the infos about the atom file */
330 char* sAtomBuffer
= NULL
; /* buffer that will contain the content of the atom file */
331 char* sAtomBufferPtr1
= NULL
; /* pointer used to parse the atom buffer */
332 char* sAtomBufferPtr2
= NULL
; /* pointer used to parse the atom buffer */
333 char sMsg
[1024]; /* buffer used to write trace messages */
335 /* try to open the atom file */
336 sprintf(sMsg
, "Atom file path: %s\n", sAtomFilePath
); trace(sMsg
);
337 fAtomFile
= fopen(sAtomFilePath
, "r");
338 if (fAtomFile
== NULL
){
339 /* unable to open atom file, put "ERR: in the new mesages counter window */
340 trace("Error opening atom file.\n");
342 writeString("ERR", COUNTERWINDOW
);
345 /* atom file opened!Get its size */
346 stat(sAtomFilePath
, &statBuffer
);
347 fdAtomFile
= fileno(fAtomFile
);
348 sprintf(sMsg
, "Atom file size : %i\n", (int) statBuffer
.st_size
); trace(sMsg
);
350 /* allocate memory for a buffer the size of the atom file */
351 sAtomBuffer
= malloc(((size_t) statBuffer
.st_size
) + 1);
353 /* read the content of the atom file and terminate the string in the buffer */
354 read(fdAtomFile
, sAtomBuffer
, (size_t) statBuffer
.st_size
);
355 sAtomBuffer
[((int) statBuffer
.st_size
)] = '\0';
357 /* find the <fullcount> and </fullcount> tags in the buffer then isolate the middle string */
358 sAtomBufferPtr1
= strstr(sAtomBuffer
, "<fullcount>");
359 sAtomBufferPtr2
= strstr(sAtomBuffer
, "</fullcount>");
360 iLength
= (sAtomBufferPtr2
- (sAtomBufferPtr1
+ 11));
361 strncpy(sNewMails
, sAtomBufferPtr1
+ 11, iLength
);
362 sNewMails
[iLength
] = '\0';
363 sprintf(sMsg
, "New mails count : %s\n", sNewMails
); trace(sMsg
);
365 /* for the return value... not really used but who knows the future... :P */
366 iNewMails
= atoi(sNewMails
);
368 /* If new mails counter == 0 -> grey the icon, else put it back in red */
370 copyXPMArea(74, 7, 31, 23, 16, 9);
372 copyXPMArea(74, 33, 31, 23, 16, 9);
375 /* re-create the sNewMails string putting the required '0' at the beginning */
376 sprintf(sNewMails
, "%03i\n", iNewMails
);
378 /* Update the counter */
379 writeString(sNewMails
, COUNTERWINDOW
);
381 /* close the atom file */
384 /* release the memory allocated for the atom file */
391 /* ----------------------------------------------------------------- */
392 /* Function : getAtomFile */
393 /* Usage : use wget to grab the atom file from gmail server */
395 /* Arguments: None */
397 /* Returns : Nothing */
398 /* ----------------------------------------------------------------- */
399 void getAtomFile(void){
400 char sWgetRcFile
[1024]; /* to contain the path of the .wgetrc file */
401 FILE *fWgetRcFile
; /* pointer to the .wgetrc file */
402 int iWgetRcFd
; /* to contain the file descriptor of the .wgetrc file */
403 int iWgetRC
; /* the return code of the wget command */
404 int iRcFileExists
= 0; /* flag to know if there was an exising .wgetrc file */
405 struct stat statBuf
; /* the buffer for the call of the stat command */
406 char sBuff
[1024]; /* buffer */
407 char sMsg
[1024]; /* used for tracing messages purpose */
409 /* set the lastPoll value to the current time */
410 lastPoll
= time(NULL
);
412 /* test if there is already a .wgetrc file. If so, move it temporarily */
413 sprintf(sWgetRcFile
, "%s/.wgetrc", sHomeDir
);
414 sprintf(sMsg
, ".wgetrc file path : %s\n", sWgetRcFile
); trace(sMsg
);
415 if(!stat(sWgetRcFile
, &statBuf
)){
416 /* if stat returned 0, the file exists, process to the move */
418 trace("The .wgetrc file exists, move it temporarily\n");
419 sprintf(sBuff
, "%s/.wgetrc", sWorkDir
);
420 rename(sWgetRcFile
, sBuff
);
422 trace("No .wgetrc file found, good thing! :P\n");
425 /* open the .wgetrc file for writing */
426 fWgetRcFile
= fopen(sWgetRcFile
, "w");
427 if(fWgetRcFile
!= NULL
){
428 iWgetRcFd
= fileno(fWgetRcFile
);
430 /* write its required content (gmail username and password) then close it */
431 sprintf(sBuff
, "--user=%s\n--password=%s\n", sUname
, sPass
);
432 write(iWgetRcFd
, sBuff
, strlen(sBuff
));
436 /* prepare the WGET call (supress output when not in debugging mode) */
439 "wget --no-check-certificate https://mail.google.com/mail/feed/atom -O %s/atom",
443 "wget -q --no-check-certificate https://mail.google.com/mail/feed/atom -O %s/atom",
448 iWgetRC
= system(sBuff
);
450 /* if the command returns a non-zero integer, something failed, delete the atom file */
455 /* erase the .wgetrc file */
458 /* If we backed-up an existing .wgetrc file, bring it back */
460 trace("Brining back original .wgetrc file\n");
461 sprintf(sBuff
, "%s/.wgetrc", sWorkDir
);
462 rename(sBuff
, sWgetRcFile
);
465 trace("Unable to open .wgetrc file for writing\n");
467 /* system("cp /tmp/atom /home/sig/.wmckgmail/atom"); */
470 /* ----------------------------------------------------------------- */
471 /* Function : initialize */
472 /* Usage : Read the config file, initialize global variables, ... */
474 /* Arguments: None */
476 /* Returns : Nothing */
477 /* ----------------------------------------------------------------- */
478 void initialize(void){
479 char sMsg
[1024]; /* buffer used for tracing purposes */
480 FILE *fCfgFile
; /* File pointer to the config file */
481 size_t len
= 0; /* used by the getline function */
482 char *sBuff
; /* buffer used by the getline function */
483 int iGetLineRes
; /* to get the result code of the getline function */
484 char sAttr
[20]; /* to contain attributes names read in the config file */
485 char sVal
[256]; /* to contain attributes values read in the config file */
486 char sPollInterval
[5]; /* where we keep the poll interval in string form */
488 trace("----- Initializing application -----\n");
490 /* Get the home directory of the user */
491 strcpy(sHomeDir
, getenv("HOME"));
492 sprintf(sMsg
, "User home directory : %s\n", sHomeDir
); trace(sMsg
);
494 /* Set the working directory */
495 sprintf(sWorkDir
, "%s/.wmckgmail", sHomeDir
);
496 sprintf(sMsg
, "Working directory : %s\n", sWorkDir
); trace(sMsg
);
498 /* Set the path of the atom file */
499 sprintf(sAtomFile
, "%s/atom", sWorkDir
);
500 sprintf(sMsg
, "Atom file path : %s\n", sAtomFile
); trace(sMsg
);
502 /* Set the path of the config file */
503 sprintf(sConfigFile
, "%s/config", sWorkDir
);
504 sprintf(sMsg
, "Config file path : %s\n", sConfigFile
); trace(sMsg
);
506 /* try to open the config file */
507 fCfgFile
= fopen(sConfigFile
, "r");
508 if(fCfgFile
== NULL
){
509 /* config file not found, abort the program */
510 sprintf(sMsg
, "** ERROR ** Cannot open config file : \"%s\"\n Program aborted.", sConfigFile
);
511 printf("\n%s\n\n", sMsg
);
515 /* ok, let's now read the config file and set the attributes read from it */
516 trace("\nReading config file :\n");
518 iGetLineRes
= getline(&sBuff
, &len
, fCfgFile
);
519 while(iGetLineRes
!= EOF
){
520 sprintf(sMsg
, " _____\n Read line : %s", sBuff
); trace(sMsg
);
521 sscanf(sBuff
, "%s %s", sAttr
, sVal
);
522 sprintf(sMsg
, " Attribute : %s\n Value : %s\n", sAttr
, sVal
); trace(sMsg
);
524 if(!strcmp(sAttr
, "uname")){
525 strcpy(sUname
, sVal
);
526 trace(" * Got username!\n");
527 } else if(!strcmp(sAttr
, "pass")){
529 trace(" * Got password!\n");
530 } else if(!strcmp(sAttr
, "pollinterval")){
531 trace(" * Got poll interval!\n");
532 iPollInterval
= atoi(sVal
);
533 if(iPollInterval
< 1 || iPollInterval
> 6000) {
534 trace(" - Warning - Value not understood, setting to 60 seconds\n");
537 strcpy(sPollInterval
, sVal
);
539 } else if(!strcmp(sAttr
, "browsercmd1")){
542 /* use sBuff because sAttr is not the full line */
543 sprintf(sMsg
, " Full line : %s", (sBuff
+ (strlen(sAttr
)) + 1)); trace(sMsg
);
544 trace(" * Got browser launch command #1 !\n");
545 trace("\n Searching for the $@ string in the line\n");
546 if((ptr
= strchr(sBuff
, (int) '$')) != NULL
){
547 trace(" Found '$' ! Now verifying if followed by '@'\n");
548 /* found '$', verify if followed by '@' (to instruct to replace $@ by gmail url)*/
549 if((ptr
+ 1)[0] == '@'){
550 /*system("mozilla -remote \"openURL(http://www.gmail.com)\"");*/
551 trace(" Found '@' ! let's replace it by http://www.gmail.com\n");
552 strncpy(sBrowserCmd1
, (sBuff
+ (strlen(sAttr
) + 1)),
553 ((ptr
- 1) - (sBuff
+ strlen(sAttr
))));
554 sBrowserCmd1
[ptr
- sBuff
] = '\0';
555 sprintf(sBrowserCmd1
, "%s%s%s", sBrowserCmd1
, "http://www.gmail.com",
556 sBuff
+ strlen(sBrowserCmd1
) + strlen(sAttr
) + 3);
558 strcpy(sBrowserCmd1
, ptr
);
561 strcpy(sBrowserCmd1
, (sBuff
+ (strlen(sAttr
) + 1)));
563 sprintf(sMsg
, " Browser Command #1 is : %s\n", sBrowserCmd1
); trace(sMsg
);
565 } else if(!strcmp(sAttr
, "browsercmd2")){
568 /* use sBuff because sAttr is not the full line */
569 sprintf(sMsg
, " Full line : %s", (sBuff
+ (strlen(sAttr
)) + 1)); trace(sMsg
);
570 trace(" * Got browser launch command #2 !\n");
571 trace("\n Searching for the $@ string in the line\n");
572 if((ptr
= strchr(sBuff
, (int) '$')) != NULL
){
573 trace(" Found '$' ! Now verifying if followed by '@'\n");
574 /* found '$', verify if followed by '@' (to instruct to replace $@ by gmail url)*/
575 if((ptr
+ 1)[0] == '@'){
576 /*system("mozilla -remote \"openURL(http://www.gmail.com)\"");*/
577 trace(" Found '@' ! let's replace it by http://www.gmail.com\n");
578 strncpy(sBrowserCmd2
, (sBuff
+ (strlen(sAttr
) + 1)),
579 ((ptr
- 1) - (sBuff
+ strlen(sAttr
))));
580 sBrowserCmd2
[ptr
- sBuff
] = '\0';
581 sprintf(sBrowserCmd2
, "%s%s%s", sBrowserCmd2
, "http://www.gmail.com",
582 sBuff
+ strlen(sBrowserCmd2
) + strlen(sAttr
) + 3);
584 strcpy(sBrowserCmd2
, ptr
);
587 strcpy(sBrowserCmd2
, (sBuff
+ (strlen(sAttr
) + 1)));
589 sprintf(sMsg
, " Browser Command #2 is : %s\n", sBrowserCmd2
); trace(sMsg
);
592 trace(" ** Warning ** Line not understood, skipped.\n");
595 iGetLineRes
= getline(&sBuff
, &len
, fCfgFile
);
599 /* Verify that we got all the required parameters from the config file */
600 trace("Validating config file parameters\n\n");
601 if(!strcmp(sUname
, "")){
602 printf(" ** ERROR ** Username not found in config file.\n Program aborted.\n");
606 if(!strcmp(sPass
, "")){
607 printf(" ** ERROR ** Password not found in config file.\n Program aborted.\n");
611 if(!strcmp(sPollInterval
, "")){
612 printf(" ** Warning ** Poll interval not defined, setting to 5 minutes.\n\n");
616 /* release getline buffer memory */
620 trace("----- Initialization completed -----\n");