Strip off version numbers from dir name
[dockapps.git] / wmckgmail / wmckgmail / wmckgmail.c
bloba98c8b7b2cee184babda5480647895b50fc7b848
1 /****************************************************************************/
2 /* wmckgmail 1.1 : A dockapp to monitor the number of unread mails in a */
3 /* gmail inbox */
4 /* Author : Sylvain Tremblay <stremblay@gmail.com> */
5 /* Release date : Sep 09, 2006 */
6 /****************************************************************************/
8 /* -------------------- */
9 /* Defines */
10 /* -------------------- */
11 #define _GNU_SOURCE
12 #define DEBUG 0
13 #define COUNTERWINDOW 0
14 #define BIG_M_ 0
16 /* -------------------- */
17 /* Includes */
18 /* -------------------- */
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <ctype.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <unistd.h>
26 #include <time.h>
28 #include <X11/Xlib.h>
29 #include <X11/xpm.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 */
64 Display *display;
65 int screen_num;
67 static char *progname;
69 /* -------------------- */
70 /* main() */
71 /* -------------------- */
72 int main(int argc, char **argv){
73 XEvent Event;
74 int but_stat = -1;
75 int i;
76 char sMsg[1024]; /* used to create tracing messages */
78 progname = argv[0];
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);
86 RedrawWindow();
88 /* Read config file and initialize variables */
89 initialize();
91 /* Do a first verification out of the main loop */
92 getAtomFile();
93 getNewMailsCount(sAtomFile);
95 /* event loop */
97 while(1){
98 RedrawWindow();
100 /* Update every iPollInterval seconds */
101 if((time(NULL) - lastPoll) >= (time_t) iPollInterval){
102 getAtomFile();
103 getNewMailsCount(sAtomFile);
105 if(DEBUG) system("date\necho \"-----\"");
108 while(XPending(display)){
109 XNextEvent(display, &Event);
110 switch(Event.type){
111 case Expose:
112 RedrawWindow();
113 break;
115 case DestroyNotify:
116 XCloseDisplay(display);
117 exit(0);
119 case ButtonPress:
120 i = CheckMouseRegion(Event.xbutton.x, Event.xbutton.y);
121 but_stat = i;
122 sprintf(sMsg, "button pressed! region index is: %i\n", but_stat); trace(sMsg);
123 break;
125 case ButtonRelease:
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);
131 switch(but_stat){
132 case BIG_M_:
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);
144 } else {
145 trace("Command succed (returned 0)\n");
147 } else {
148 trace("No browsercmd1 nor browsercmd2 defined, nothing to do.\n");
150 trace("\n");
151 break;;
153 case 1:
154 /* action for region 1 */
155 break;;
158 but_stat = -1;
159 break;
162 usleep(100000);
166 /* ----------------------------------------------------------------- */
167 /* Function : writeString */
168 /* Usage : Write a string in a specific window */
169 /* */
170 /* Arguments: sString -> The string to write */
171 /* iWhichWindow -> The window to write to */
172 /* */
173 /* Returns : Nothing */
174 /* ----------------------------------------------------------------- */
175 void writeString(char* sString, int iWhichWindow){
176 int i;
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);
183 } else {
184 writeChar(sString[i], iWhichWindow, i);
189 /* ----------------------------------------------------------------- */
190 /* Function : writeChar */
191 /* Usage : Write a character in a specific window at a specific */
192 /* position */
193 /* */
194 /* Arguments: cTheChar -> Character to write */
195 /* iWhichWindow -> The window to write to */
196 /* iPos -> The position of the character */
197 /* */
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){
212 iSrcX = 1;
213 iSrcY = 64;
214 iDestX = 23;
215 iDestY = 39;
216 iWidth = 6;
217 iHeight = 9;
218 iMaxPos = 2;
219 } else {
220 return;
223 /* If trying to write pass the iMaxPos position, exit the function */
224 if(iPos > iMaxPos){
225 return;
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){
237 return;
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 */
248 /* position */
249 /* */
250 /* Arguments: cTheNum -> Number to write */
251 /* iWhichWindow -> The window to write to */
252 /* iPos -> The position of the number */
253 /* */
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){
268 iSrcX = 1;
269 iSrcY = 76;
270 iDestX = 23;
271 iDestY = 39;
272 iWidth = 6;
273 iHeight = 9;
274 iMaxPos = 9;
275 } else {
276 return;
279 /* If trying to write pass the iMaxPos position, exit the function */
280 if(iPos > iMaxPos){
281 return;
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){
293 return;
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 */
303 /* NULL. */
304 /* */
305 /* Arguments: sMsg -> The message to output */
306 /* */
307 /* Returns : Nothing */
308 /* ----------------------------------------------------------------- */
309 void trace(char *sMsg){
310 if(DEBUG){
311 printf("%s", sMsg);
315 /* ----------------------------------------------------------------- */
316 /* Function : getNewMailsCount */
317 /* Usage : Read the number of new mails from the atom file */
318 /* */
319 /* Arguments: sAtomFilePath -> Path to the atom file */
320 /* */
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");
341 iNewMails = -1;
342 writeString("ERR", COUNTERWINDOW);
344 } else {
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 */
369 if(iNewMails == 0){
370 copyXPMArea(74, 7, 31, 23, 16, 9);
371 } else {
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 */
382 fclose(fAtomFile);
384 /* release the memory allocated for the atom file */
385 free(sAtomBuffer);
388 return(iNewMails);
391 /* ----------------------------------------------------------------- */
392 /* Function : getAtomFile */
393 /* Usage : use wget to grab the atom file from gmail server */
394 /* */
395 /* Arguments: None */
396 /* */
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 */
417 iRcFileExists = 1;
418 trace("The .wgetrc file exists, move it temporarily\n");
419 sprintf(sBuff, "%s/.wgetrc", sWorkDir);
420 rename(sWgetRcFile, sBuff);
421 } else {
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));
434 fclose(fWgetRcFile);
436 /* prepare the WGET call (supress output when not in debugging mode) */
437 if(DEBUG){
438 sprintf(sBuff,
439 "wget --no-check-certificate https://mail.google.com/mail/feed/atom -O %s/atom",
440 sWorkDir);
441 } else {
442 sprintf(sBuff,
443 "wget -q --no-check-certificate https://mail.google.com/mail/feed/atom -O %s/atom",
444 sWorkDir);
447 /* call wget */
448 iWgetRC = system(sBuff);
450 /* if the command returns a non-zero integer, something failed, delete the atom file */
451 if(iWgetRC != 0){
452 remove(sAtomFile);
455 /* erase the .wgetrc file */
456 remove(sWgetRcFile);
458 /* If we backed-up an existing .wgetrc file, bring it back */
459 if(iRcFileExists){
460 trace("Brining back original .wgetrc file\n");
461 sprintf(sBuff, "%s/.wgetrc", sWorkDir);
462 rename(sBuff, sWgetRcFile);
464 } else {
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, ... */
473 /* */
474 /* Arguments: None */
475 /* */
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);
512 exit(1);
514 } else {
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")){
528 strcpy(sPass, sVal);
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");
535 iPollInterval = 300;
536 } else {
537 strcpy(sPollInterval, sVal);
539 } else if(!strcmp(sAttr, "browsercmd1")){
541 char* ptr = NULL;
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);
557 } else {
558 strcpy(sBrowserCmd1, ptr);
560 } else {
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")){
567 char* ptr = NULL;
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);
583 } else {
584 strcpy(sBrowserCmd2, ptr);
586 } else {
587 strcpy(sBrowserCmd2, (sBuff + (strlen(sAttr) + 1)));
589 sprintf(sMsg, " Browser Command #2 is : %s\n", sBrowserCmd2); trace(sMsg);
591 } else {
592 trace(" ** Warning ** Line not understood, skipped.\n");
595 iGetLineRes = getline(&sBuff, &len, fCfgFile);
597 trace("\n");
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");
603 exit(2);
606 if(!strcmp(sPass, "")){
607 printf(" ** ERROR ** Password not found in config file.\n Program aborted.\n");
608 exit(3);
611 if(!strcmp(sPollInterval, "")){
612 printf(" ** Warning ** Poll interval not defined, setting to 5 minutes.\n\n");
613 iPollInterval = 300;
616 /* release getline buffer memory */
617 free(sBuff);
620 trace("----- Initialization completed -----\n");