/* * options.c * * Low-level option handling * * (c) 2002-2005 Thomas White * Part of TuxMessenger - GTK+-based MSN Messenger client * * This package is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 dated June, 1991. * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include "debug.h" #include "accountwindow.h" #include "options.h" #include "msngenerics.h" #include "mainwindow.h" #include "error.h" #include "routines.h" #include "listcache.h" #define DEFAULT_HOSTNAME "messenger.hotmail.com" #define DEFAULT_WGET "/usr/bin/wget" static struct { char *username; char *password; unsigned int rememberlogindetails; char *hostname; unsigned int port; char *wget; char *gtc; /* Special - comes from list cache. */ char *blp; /* Special - comes from list cache. */ unsigned int hideoffline; /* Hide offline contacts? */ unsigned int hidecsm; /* Hide local CSM? */ GdkColor *localcolour_gdk; /* Colour for local user's messages. */ char *localcolour_string; /* String version of the above. */ unsigned int ofontoverride; /* Override other contacts' chosen fonts/colours? */ GdkColor *ocolour_gdk; /* Colour to override contacts' messages with. */ char *localfont; /* Font for local user's messages. */ char *ofont; /* Font to use for contacts' messages (if overriding). */ unsigned int ircstyle; /* Use IRC style for new IM windows? */ unsigned int timestamps; /* Show timestamps for messages in new IM windows? */ unsigned int showavatars; /* Display avatars in new IM windows? */ unsigned int showemoticons; /* Show emoticons in new IM windows? */ } options = { NULL, /* Username */ NULL, /* Password */ 0, /* Remember login details */ NULL, /* Hostname */ 0, /* Port */ NULL, /* Wget */ NULL, /* GTC */ NULL, /* BLP */ FALSE, /* Hide offline */ FALSE, /* Hide CSM */ NULL, /* localcolour_gdk */ NULL, /* localcolour_string */ FALSE, /* ofontoverride */ NULL, /* ocolour_gdk */ NULL, /* localfont */ NULL, /* ofont */ 0, /* ircstyle */ 0, /* timestamps */ 0, /* showavatars */ 0 /* showemoticons */ }; static void options_setdefaults() { options.username = strdup(""); options.password = strdup(""); options.rememberlogindetails = 0; options.hostname = strdup(DEFAULT_HOSTNAME); options.port = DEFAULT_NS_PORT; options.wget = strdup(DEFAULT_WGET); options.gtc = strdup("BL"); options.blp = strdup("A"); options.hideoffline = FALSE; options.hidecsm = FALSE; options.ofontoverride = FALSE; options.localcolour_gdk = NULL; options.localcolour_string = NULL; options.ocolour_gdk = NULL; options.localfont = NULL; options.ofont = NULL; options.ircstyle = 0; options.timestamps = 0; options.showavatars = 0; options.showemoticons = 0; } static void options_createhomedir(char *filename) { char *av_name; char *hg_name; /* Create directory. */ if ( mkdir(filename, S_IRUSR | S_IWUSR | S_IXUSR) != 0 ) { debug_print("OP: Couldn't create ~/.tuxmessenger directory.\n"); exit(1); } /* Create "avatars" subdirectory. */ av_name = malloc(strlen(filename) + 9); strcpy(av_name, filename); strcat(av_name, "/avatars"); if ( mkdir(av_name, S_IRUSR | S_IWUSR | S_IXUSR) != 0 ) { debug_print("OP: Couldn't create ~/.tuxmessenger/avatars directory.\n"); exit(1); } free(av_name); /* Link the "Hourglass", "Default" and "Own" avatars. */ hg_name = malloc(strlen(filename) + 20); strcpy(hg_name, filename); strcat(hg_name, "/wait_avatar.png"); if ( symlink(DATADIR"/tuxmessenger/hourglass.png", hg_name) != 0 ) { debug_print("OP: Couldn't create ~/.tuxmessenger/wait_avatar.png.\n"); exit(1); } strcpy(hg_name, filename); strcat(hg_name, "/default_avatar.png"); if ( symlink(DATADIR"/tuxmessenger/no_avatar.png", hg_name) != 0 ) { debug_print("OP: Couldn't create ~/.tuxmessenger/default_avatar.png.\n"); exit(1); } strcpy(hg_name, filename); strcat(hg_name, "/avatar.png"); if ( symlink(DATADIR"/tuxmessenger/no_avatar.png", hg_name) != 0 ) { debug_print("OP: Couldn't create ~/.tuxmessenger/avatar.png.\n"); exit(1); } free(hg_name); } static void options_bailout() { options_setdefaults(); accountwindow_open(); } void options_setusername(const char *username) { unsigned int changed = FALSE; if ( (username != NULL) && (options.username != NULL) && (strcmp(options.username, "") != 0) && (strcmp(username, options.username) != 0) ) { changed = TRUE; } if ( options.username != NULL ) { free(options.username); } options.username = strdup(username); if ( changed ) { listcache_setcsm(""); listcache_invalidate(); } } void options_setpassword(const char *password) { if ( options.password != NULL ) { free(options.password); } options.password = strdup(password); } void options_sethostname(const char *hostname) { if ( options.hostname != NULL ) { free(options.hostname); } if ( strlen(hostname) > 0 ) { options.hostname = strdup(hostname); } else { debug_print("OP: Using default server hostname instead of zero-length string.\n"); options.hostname = strdup(DEFAULT_HOSTNAME); } } void options_setport(const int port) { options.port = port; if ( options.port == 0 ) { /* That's just plain silly. */ debug_print("OP: Correcting port number: 0->%i.\n", DEFAULT_NS_PORT); options_setport(DEFAULT_NS_PORT); } if ( options.port > 65535 ) { /* D'oh */ debug_print("OP: Port number too large: correcting to %i\n", DEFAULT_NS_PORT); options_setport(DEFAULT_NS_PORT); } } void options_setwget(const char *wget) { if ( options.wget != NULL ) { free(options.wget); } if ( strlen(wget) > 0 ) { options.wget = strdup(wget); } else { debug_print("OP: Using default wget instead of zero-length string.\n"); options.wget = strdup(DEFAULT_WGET); } } void options_setrememberlogindetails(unsigned int remember) { options.rememberlogindetails = remember; } void options_sethideoffline(unsigned int hide) { options.hideoffline = hide; } void options_sethidecsm(unsigned int hide) { options.hidecsm = hide; } void options_setlocalcolour_gdk(const GdkColor *colour) { char *string; if ( options.localcolour_gdk != NULL ) { gdk_color_free(options.localcolour_gdk); } if ( options.localcolour_string != NULL ) { free(options.localcolour_string); } options.localcolour_gdk = gdk_color_copy(colour); /* Now work out the string version */ string = malloc(7); /* Yukky BGR order instead of RGB */ snprintf(string, 7, "%02hhx%02hhx%02hhx", colour->blue >> 8, colour->green >> 8, colour->red >> 8); options.localcolour_string = string; debug_print("OP: String value '%s'\n", string); } void options_setlocalcolour_string(const char *colour) { char *flipped; char *string; GdkColor gdkcolour; if ( options.localcolour_gdk != NULL ) { gdk_color_free(options.localcolour_gdk); } if ( options.localcolour_string != NULL ) { free(options.localcolour_string); } options.localcolour_string = strdup(colour); /* Now work out the GDK version - flip then parse */ flipped = routines_flipcolour(colour); string = malloc(8); strcpy(string, "#"); strncat(string, flipped, 7); string[7] = '\0'; free(flipped); if ( gdk_color_parse(string, &gdkcolour) ) { options.localcolour_gdk = gdk_color_copy(&gdkcolour); } else { debug_print("OP: Whoops! Colour parsing failed (%s)...\n", string); if ( options.localcolour_gdk != NULL ) { gdk_color_free(options.localcolour_gdk); } if ( options.localcolour_string != NULL ) { free(options.localcolour_string); } options.localcolour_string = NULL; options.localcolour_gdk = NULL; } free(string); } void options_setocolour_gdk(const GdkColor *colour) { if ( options.ocolour_gdk != NULL ) { gdk_color_free(options.ocolour_gdk); } options.ocolour_gdk = gdk_color_copy(colour); } void options_setofontoverride(unsigned int override) { options.ofontoverride = override; } void options_setocolour_string(const char *colour) { char *string; GdkColor gdkcolour; if ( options.ocolour_gdk != NULL ) { gdk_color_free(options.localcolour_gdk); } string = malloc(8); strcpy(string, "#"); strncat(string, colour, 7); string[7] = '\0'; if ( gdk_color_parse(string, &gdkcolour) ) { options.ocolour_gdk = gdk_color_copy(&gdkcolour); } else { debug_print("OP: Whoops! Colour parsing failed (%s)...\n", string); options.localcolour_gdk = NULL; } free(string); } void options_setlocalfont(const char *font) { options.localfont = strdup(font); } void options_setofont(const char *font) { options.ofont = strdup(font); } void options_setircstyle(unsigned int ircstyle) { options.ircstyle = ircstyle; } void options_settimestamps(unsigned int timestamps) { options.timestamps = timestamps; } void options_setshowavatars(unsigned int showavatars) { options.showavatars = showavatars; } void options_setshowemoticons(unsigned int showemoticons) { options.showemoticons = showemoticons; } void options_load() { int glob_retval; glob_t glob_result; struct stat stat_buffer; struct stat *statbuf; char *dir_filename; char *rc_filename; glob_retval = glob("~", GLOB_TILDE, NULL, &glob_result); statbuf = &stat_buffer; if ( glob_retval != 0 ) { debug_print("OP: glob() for ~/.tuxmessenger failed: "); switch ( glob_retval ) { case GLOB_NOSPACE : debug_print("GLOB_NOSPACE\n"); break; case GLOB_ABORTED : debug_print("GLOB_ABORTED\n"); break; case GLOB_NOMATCH : debug_print("GLOB_NOMATCH\n"); break; default : debug_print("Unknown!\n"); break; } debug_print("OP: Can't continue :(\n"); exit(1); } dir_filename = malloc(strlen(glob_result.gl_pathv[0]) + 15); strcpy(dir_filename, glob_result.gl_pathv[0]); globfree(&glob_result); strcat(dir_filename, "/.tuxmessenger"); if ( stat(dir_filename, statbuf) != -1 ) { if ( S_ISDIR(stat_buffer.st_mode) ) { debug_print("OP: Found '%s'.\n", dir_filename); /* All is good. */ } else { debug_print("OP: Found '%s', but it isn't a directory!\n", dir_filename); debug_print("OP: Can't continue :(\n"); exit(1); } } else { debug_print("OP: ~/.tuxmessenger directory not found: creating it.\n"); options_createhomedir(dir_filename); } /* Right - after all of that, let's take a look at the central config file. */ options_setdefaults(); /* Set defaults to begin with. */ rc_filename = malloc(strlen(dir_filename) + 8); strcpy(rc_filename, dir_filename); strcat(rc_filename, "/config"); if ( stat(rc_filename, statbuf) != -1 ) { if ( (S_ISREG(stat_buffer.st_mode)) || (S_ISLNK(stat_buffer.st_mode)) ) { /* Yay :D */ FILE *fh; char *line; char *rval; int whoops = 0; fh = fopen(rc_filename, "r"); if ( fh == NULL ) { debug_print("OP: Error opening options file.\n"); options_bailout(); free(rc_filename); free(dir_filename); return; } line = malloc(1024); assert(line != NULL); rval = "hello"; /* Non-NULL */ while ( rval ) { rval = fgets(line, 1023, fh); if ( ferror(fh) && !feof(fh) ) { whoops = 1; break; } if ( strlen(line) > 1 ) { if ( line[strlen(line)-1] == '\n' ) { line[strlen(line)-1] = '\0'; /* Cut off trailing newline. */ } } else { line[0] = '\0'; } if ( strncmp(line, "username ", 9) == 0 ) { debug_print("OP: Got username from config file.\n"); options_setusername(line+9); } if ( strncmp(line, "password ", 9) == 0 ) { debug_print("OP: Got password from config file.\n"); options_setpassword(line+9); } if ( strncmp(line, "hostname ", 9) == 0 ) { debug_print("OP: Got server hostname from config file.\n"); options_sethostname(line+9); } if ( strncmp(line, "port ", 5) == 0 ) { debug_print("OP: Got server port number from config file.\n"); options_setport(atoi(line+5)); } if ( strncmp(line, "wget ", 5) == 0 ) { debug_print("OP: Got wget command from config file.\n"); options_setwget(line+5); } if ( strncmp(line, "hide_offline", 12) == 0 ) { debug_print("OP: Got hide_offline from config file.\n"); options_sethideoffline(TRUE); } if ( strncmp(line, "hide_localcsm", 12) == 0 ) { debug_print("OP: Got hide_localcsm from config file.\n"); options_sethidecsm(TRUE); } if ( strncmp(line, "localcolour ", 12) == 0 ) { debug_print("OP: Got local colour from config file.\n"); options_setlocalcolour_string(line+12); } if ( strncmp(line, "ofontoverride", 12) == 0 ) { debug_print("OP: Got ofontoverride from config file.\n"); options_setofontoverride(TRUE); } if ( strncmp(line, "ocolour ", 8) == 0 ) { debug_print("OP: Got override colour from config file.\n"); options_setocolour_string(line+8); } if ( strncmp(line, "localfont ", 10) == 0 ) { debug_print("OP: Got localfont from config file.\n"); options_setlocalfont(line+10); } if ( strncmp(line, "ofont ", 6) == 0 ) { debug_print("OP: Got ofont from config file.\n"); options_setofont(line+6); } if ( strncmp(line, "show_avatars", 12) == 0 ) { debug_print("OP: Got show_avatars from config file.\n"); options_setshowavatars(TRUE); } if ( strncmp(line, "show_emoticons", 14) == 0 ) { debug_print("OP: Got show_emoticons from config file.\n"); options_setshowemoticons(TRUE); } if ( strncmp(line, "timestamps", 10) == 0 ) { debug_print("OP: Got timestamps from config file.\n"); options_settimestamps(TRUE); } if ( strncmp(line, "ircstyle", 8) == 0 ) { debug_print("OP: Got ircstyle from config file.\n"); options_setircstyle(TRUE); } line[0] = '\0'; /* Prevent this line from being parsed twice. */ } free(line); fclose(fh); if ( whoops ) { debug_print("OP: Error reading options file.\n"); options_bailout(); } } else { /* FFS. I'm not handling that. */ debug_print("OP: Options file exists but isn't a file!\n"); exit(1); } } else { /* Options file isn't there. Set "sensible defaults" and open the config window... */ debug_print("OP: Options file not found.\n"); options_bailout(); } options_setrememberlogindetails(1); /* If the username and password are blank, and the server is set to its default, we assume "Remember login details" was unticked last time. */ if ( (strlen(options_username())==0) && (strlen(options_password())==0) && (options_port()==DEFAULT_NS_PORT) && (strcmp(options_hostname(), DEFAULT_HOSTNAME)==0)) { options_setrememberlogindetails(0); } free(dir_filename); free(rc_filename); } const char *options_username() { /* FIXME: Check length */ /* Don't forget to change to lower case. */ return options.username; } const char *options_password() { return options.password; } const char *options_hostname() { return options.hostname; } unsigned short int options_port() { return options.port; } const char *options_wget() { return options.wget; } const char *options_blp() { return options.blp; } const char *options_gtc() { return options.gtc; } unsigned int options_rememberlogindetails() { return options.rememberlogindetails; } unsigned int options_hideoffline() { return options.hideoffline; } unsigned int options_hidecsm() { return options.hidecsm; } const GdkColor *options_localcolour_gdk() { return options.localcolour_gdk; } const char *options_localcolour_string() { return options.localcolour_string; } unsigned int options_ofontoverride() { return options.ofontoverride; } const GdkColor *options_ocolour_gdk() { return options.ocolour_gdk; } unsigned int options_ircstyle() { return options.ircstyle; } unsigned int options_timestamps() { return options.timestamps; } unsigned int options_showavatars() { return options.showavatars; } unsigned int options_showemoticons() { return options.showemoticons; } static char *options_ocolour_string() { char *string; const GdkColor *colour = options_ocolour_gdk(); if ( colour == NULL ) { return NULL; } string = malloc(7); /* RGB order here */ snprintf(string, 7, "%02hhx%02hhx%02hhx", colour->red >> 8, colour->green >> 8, colour->blue >> 8); return string; } const char *options_localfont() { return options.localfont; } const char *options_ofont() { return options.ofont; } void options_save() { FILE *fh; int glob_retval; glob_t glob_result; struct stat stat_buffer; struct stat *statbuf; char *dir_filename; char *rc_filename; int whoops = 0; char *newline; const char *ousername; const char *opassword; const char *ohostname; int oport; glob_retval = glob("~", GLOB_TILDE, NULL, &glob_result); statbuf = &stat_buffer; if ( glob_retval != 0 ) { debug_print("OP: glob() for ~/.tuxmessenger failed: "); switch ( glob_retval ) { case GLOB_NOSPACE : debug_print("GLOB_NOSPACE\n"); break; case GLOB_ABORTED : debug_print("GLOB_ABORTED\n"); break; case GLOB_NOMATCH : debug_print("GLOB_NOMATCH\n"); break; default : debug_print("Unknown!\n"); break; } debug_print("OP: Couldn't save options :(\n"); return; } dir_filename = malloc(strlen(glob_result.gl_pathv[0]) + 15); strcpy(dir_filename, glob_result.gl_pathv[0]); globfree(&glob_result); strcat(dir_filename, "/.tuxmessenger"); if ( stat(dir_filename, statbuf) != -1 ) { if ( S_ISDIR(stat_buffer.st_mode) ) { debug_print("OP: Found '%s'.\n", dir_filename); /* All is good. */ } else { debug_print("OP: Found '%s', but it isn't a directory!\n", dir_filename); debug_print("OP: Couldn't save options :(\n"); return; } } else { /* This shouldn't happen here unless someone's been a muppet. */ debug_print("OP: ~/.tuxmessenger directory not found: creating it.\n"); options_createhomedir(dir_filename); } rc_filename = malloc(strlen(dir_filename) + 8); strcpy(rc_filename, dir_filename); strcat(rc_filename, "/config"); fh = fopen(rc_filename, "w"); if ( chmod(rc_filename, S_IRUSR | S_IWUSR) != 0 ) { error_report("Couldn't set permissions on config file!"); } if ( fh == NULL ) { debug_print("OP: Error opening options file.\n"); debug_print("OP: Couldn't save options :(\n"); return; } newline = malloc(2); sprintf(newline, "\n"); if ( options_rememberlogindetails() ) { ousername = options_username(); opassword = options_password(); ohostname = options_hostname(); oport = options_port(); } else { ousername = ""; opassword = ""; ohostname = DEFAULT_HOSTNAME; oport = DEFAULT_NS_PORT; } /* ----------------------- Username -------------------------- */ if ( fputs("username ", fh) == EOF ) { whoops = 1; } if ( whoops == 0 ) { if ( fputs(ousername, fh) == EOF ) { whoops = 1; } } if ( whoops == 0 ) { if ( fputs(newline, fh) == EOF ) { whoops = 1; } } /* ----------------------- Password -------------------------- */ if ( whoops == 0 ) { if ( fputs("password ", fh) == EOF ) { whoops = 1; } } if ( whoops == 0 ) { if ( fputs(opassword, fh) == EOF ) { whoops = 1; } } if ( whoops == 0 ) { if ( fputs(newline, fh) == EOF ) { whoops = 1; } } /* ----------------------- wget -------------------------- */ if ( whoops == 0 ) { if ( fputs("wget ", fh) == EOF ) { whoops = 1; } } if ( whoops == 0 ) { if ( fputs(options_wget(), fh) == EOF ) { whoops = 1; } } if ( whoops == 0 ) { if ( fputs(newline, fh) == EOF ) { whoops = 1; } } /* ----------------------- Hostname -------------------------- */ if ( whoops == 0 ) { if ( fputs("hostname ", fh) == EOF ) { whoops = 1; } } if ( whoops == 0 ) { if ( fputs(ohostname, fh) == EOF ) { whoops = 1; } } if ( whoops == 0 ) { if ( fputs(newline, fh) == EOF ) { whoops = 1; } } /* ----------------------- Port -------------------------- */ if ( whoops == 0 ) { char *port; if ( fputs("port ", fh) == EOF ) { whoops = 1; } /* options_port() < 65536 because of data size - so always fits. */ port = malloc(6); sprintf(port, "%i", oport); if ( whoops == 0 ) { if ( fputs(port, fh) == EOF ) { whoops = 1; } } if ( whoops == 0 ) { if ( fputs(newline, fh) == EOF ) { whoops = 1; } } free(port); } /* ----------------------- Show Offline Contacts ---------------------- */ if ( whoops == 0 ) { if ( options_hideoffline() ) { if ( fputs("hide_offline\n", fh) == EOF ) { whoops = 1; } } /* Else write nothing. */ } /* ----------------------- Show CSM ---------------------- */ if ( whoops == 0 ) { if ( options_hidecsm() ) { if ( fputs("hide_localcsm\n", fh) == EOF ) { whoops = 1; } } /* Else write nothing. */ } /* ----------------------- Local colour -------------------------- */ if ( options_localcolour_string() != NULL ) { if ( whoops == 0 ) { if ( fputs("localcolour ", fh) == EOF ) { whoops = 1; } } if ( whoops == 0 ) { if ( fputs(options_localcolour_string(), fh) == EOF ) { whoops = 1; } } if ( whoops == 0 ) { if ( fputs(newline, fh) == EOF ) { whoops = 1; } } } /* ----------------------- Override Contacts' Fonts/Colours ---------------------- */ if ( whoops == 0 ) { if ( options_ofontoverride() ) { if ( fputs("ofontoverride\n", fh) == EOF ) { whoops = 1; } } /* Else write nothing. */ } /* ----------------------- Override colour -------------------------- */ if ( options_ocolour_string() ) { if ( whoops == 0 ) { if ( fputs("ocolour ", fh) == EOF ) { whoops = 1; } } if ( whoops == 0 ) { char *string = options_ocolour_string(); if ( fputs(string, fh) == EOF ) { whoops = 1; } free(string); } if ( whoops == 0 ) { if ( fputs(newline, fh) == EOF ) { whoops = 1; } } } /* ----------------------- Local font -------------------------- */ if ( options_localfont() ) { if ( whoops == 0 ) { if ( fputs("localfont ", fh) == EOF ) { whoops = 1; } } if ( whoops == 0 ) { if ( fputs(options_localfont(), fh) == EOF ) { whoops = 1; } } if ( whoops == 0 ) { if ( fputs(newline, fh) == EOF ) { whoops = 1; } } } /* ----------------------- Contacts' font -------------------------- */ if ( options_ofont() ) { if ( whoops == 0 ) { if ( fputs("ofont ", fh) == EOF ) { whoops = 1; } } if ( whoops == 0 ) { if ( fputs(options_ofont(), fh) == EOF ) { whoops = 1; } } if ( whoops == 0 ) { if ( fputs(newline, fh) == EOF ) { whoops = 1; } } } /* ----------------------- IRC style ---------------------- */ if ( whoops == 0 ) { if ( options_ircstyle() ) { if ( fputs("ircstyle\n", fh) == EOF ) { whoops = 1; } } /* Else write nothing. */ } /* ----------------------- Show emoticons ---------------------- */ if ( whoops == 0 ) { if ( options_showemoticons() ) { if ( fputs("show_emoticons\n", fh) == EOF ) { whoops = 1; } } /* Else write nothing. */ } /* ----------------------- Show avatars --------------------- */ if ( whoops == 0 ) { if ( options_showavatars() ) { if ( fputs("show_avatars\n", fh) == EOF ) { whoops = 1; } } /* Else write nothing. */ } /* ----------------------- Timestamps ---------------------- */ if ( whoops == 0 ) { if ( options_timestamps() ) { if ( fputs("timestamps\n", fh) == EOF ) { whoops = 1; } } /* Else write nothing. */ } if ( whoops != 0 ) { debug_print("OP: Whoops! Config file write failed.\n"); } fclose(fh); free(newline); }