Logo Search packages:      
Sourcecode: autoprofile version File versions

autoprofile.c

/*--------------------------------------------------------------------------*
 * AUTOPROFILE                                                              *
 *                                                                          *
 * A Gaim away message and profile manager that supports dynamic text       *
 *                                                                          *
 * AutoProfile is the legal property of its developers.  Please refer to    *
 * the COPYRIGHT file distributed with this source distribution.            *
 *                                                                          *
 * This program 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; either version 2 of the License, or        *
 * (at your option) any later version.                                      *
 *                                                                          *
 * This program 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 program; if not, write to the Free Software              *
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA *
 *--------------------------------------------------------------------------*/

#include "autoprofile.h"

#include "gtkdialogs.h"
#include "cmds.h"

/*--------------------------------------------------------------------------
 * FUNCTION DECLARATIONS
 *------------------------------------------------------------------------*/

/* Define global variables */
GaimPlugin *plugin_handle = NULL;
GList *components = NULL;
gboolean is_away = FALSE;
gboolean sending_auto = FALSE;
gboolean original_autoresponse = FALSE;
gpointer profile_timeout = NULL;

/* General variables */
static char *original_autoreply = NULL;

static time_t response_timeout;
static guint away_timeout = 0;

struct auto_response {
  GaimAccount *account;
  char *recepient;
  time_t time_sent;
};

static GList *auto_recepients = NULL;

/* Commands */
static GaimCmdId cmd_away_id = 0;
static GaimCmdId cmd_back_id = 0;
static GaimCmdRet cmd_away_cb (GaimConversation *, const gchar *, gchar **,
  gchar **, void *);
static GaimCmdRet cmd_back_cb (GaimConversation *, const gchar *, gchar **,
  gchar **, void *);

/*--------------------------------------------------------------------------
 * REQUIRED GAIM FUNCTIONS- INFO, INITIALIZATION, UNLOADING
 *------------------------------------------------------------------------*/
void queue_cb (
  const char *name, GaimPrefType type, gconstpointer val, gpointer data) {
  if (is_away) {
    if (gaim_prefs_get_bool("/plugins/gtk/autoprofile/queue_messages_when_away")) {
      gaim_prefs_set_string ("/gaim/gtk/conversations/im/hide_new", "away");
    } else {
      gaim_prefs_set_string ("/gaim/gtk/conversations/im/hide_new", "never");
    }
  }
}

/* What to do when plugin is loaded */
static gboolean plugin_load (GaimPlugin *plugin)
{
  int interval;
  GList *comp_list;
  struct component *comp;
 
  /* TODO: This should go into a gtk_load or something like that */
  gaim_prefs_connect_callback (
    plugin_handle, 
    "/plugins/gtk/autoprofile/queue_messages_when_away", queue_cb, NULL);

 
  /* Initialize variables */
  components = get_components ();
  is_away = FALSE;
  sending_auto = FALSE;
  response_timeout = time (NULL);

  plugin_handle = plugin;

  /* Let components initialize */
  comp_list = components;
  while (comp_list) {
    comp = comp_list->data;
    if (comp->load)        
      (comp->load) ();
    comp_list = comp_list->next;
  }
  
  /* Begin updating profiles */
  interval = 60 * 1000 * gaim_prefs_get_int (
    "/plugins/gtk/autoprofile/delay_profile");
  profile_timeout = GINT_TO_POINTER (g_timeout_add (
    interval, profile_update, NULL));
  profile_update (NULL); 

  /* Connect command detector */
  gaim_cmd_register ("away", "S", GAIM_CMD_P_PLUGIN, 
    GAIM_CMD_FLAG_IM | GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_ALLOW_WRONG_ARGS, "",
    cmd_away_cb,
    "away <text>: Activate AutoProfile away with optional text "
    "added to message", NULL);

  gaim_cmd_register ("back", "", GAIM_CMD_P_PLUGIN,
    GAIM_CMD_FLAG_IM | GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_ALLOW_WRONG_ARGS, "",
    cmd_back_cb,
    "back: Turn off AutoProfile away status", NULL);

  return TRUE;
}

/* What to do when plugin is unloaded */
static gboolean plugin_unload (GaimPlugin *plugin)
{
  GList *comp_list;
  struct component *comp;

  /* Remove global variables */
  g_source_remove (GPOINTER_TO_INT (profile_timeout));
 
  /* Unload components */
  comp_list = components;
  while (comp_list) {
    comp = comp_list->data;
    if (comp->unload)        // Make sure init_pref not null
      (comp->unload) ();
    comp_list = comp_list->next;
  } 

  /* Not sure if component contents should be free'd as well */
  g_list_free (components);
  components = NULL;

  /* Remove command detector */
  gaim_cmd_unregister (cmd_away_id);
  gaim_cmd_unregister (cmd_back_id);

  /* If away, restore */
  if (is_away)
    status_back ();

  return TRUE;
}

/* General information */
static GaimPluginInfo info =
{
  GAIM_PLUGIN_MAGIC,
  GAIM_MAJOR_VERSION,
  GAIM_MINOR_VERSION,
  GAIM_PLUGIN_STANDARD,                                 /* type           */
  GAIM_GTK_PLUGIN_TYPE,                                 /* ui_requirement */
  0,                                                    /* flags          */
  NULL,                                                 /* dependencies   */
  GAIM_PRIORITY_DEFAULT,                                /* priority       */

  N_("gtk-kluge-autoprofile"),                          /* id             */
  N_("AutoProfile"),                                    /* name           */
  AUTOPROFILE_VERSION,                                  /* version        */
  N_("User profile and away message management system"),/* summary        */
                                                            /* description    */
  N_("Allows user to set up automatically updating\n"
     "away messages and profiles that contain dynamic "
     "text"),
                                                        /* author         */
  N_("Casey Ho <caseyho at berkeley-edu>"
     "\n\t\t\taim:caseyho"),
  N_("http://hkn.eecs.berkeley.edu/~casey/"
     "autoprofile/"),                                   /* homepage       */

  plugin_load,                                          /* load           */
  plugin_unload,                                        /* unload         */
  NULL,                                                 /* destroy        */

  &ui_info,                                             /* ui_info        */
  NULL,                                                 /* extra_info     */
  NULL,                                                 /* prefs_info     */
  actions                                               /* actions        */
};

/*--------------------------------------------------------------------------
 * CORE FUNCTIONS                                          
 *------------------------------------------------------------------------*/

/* The workhorse generation function ! */
char *autoprofile_generate (const char *format) {
  char *output, *single_char, *cur_addition, *cur_addition_copy;
  
  GList *search_list;
  struct component *cur_comp;
  int space_left;
  int state;

  /* Set up strings */
  output = (char *)malloc (sizeof(char) * AP_SIZE_MAXIMUM);
  *output = '\0';
  single_char = (char *)malloc(2);
  *(single_char + 1) = '\0';
  *single_char = 'a';

  space_left = AP_SIZE_MAXIMUM - 1;

  /* When previous char was a %, state is 1, otherwise 0 */
  state = 0;
  /* Process the format string, one char at a time! */
  while (*format && space_left > 0) {
    if (state == 1) {
      /* Handle percentage value */
      if (*format == '%') {
        /* Copy char straight over */
        *single_char = *format++;
        strcat (output, single_char);
        space_left--;
      } else if (*format == 'a') {
        /* Copy string in */
        cur_addition = (char *)gaim_prefs_get_string (
          "/plugins/gtk/autoprofile/added_text");
        if (strlen (cur_addition) < space_left) {
          strcat (output, cur_addition);
          space_left = space_left - strlen (cur_addition);
        } else {
          auto_debug ("generate", "added text too large, dropped");
        }
        format++;
      } else if (*format == 'n' ||
                 *format == 't' ||
                 *format == 'd') {
        /* Copy %char straight over */
        if (space_left > 1) {
          *single_char = '%';
          strcat (output, single_char);
          *single_char = *format++;
          strcat (output, single_char);
          space_left = space_left - 2;
        }
      } else {
        /* Generate some string associated with char */
        search_list = components;
        while (TRUE) {
          if (search_list == NULL) {
            auto_debug ("generate", "no matching component found ");
            if (space_left < 2)
              break;
            *single_char = '%';
            strcat (output, single_char);
            *single_char = *format;
            strcat (output, single_char);
            space_left = space_left - 2;
            break;
          } else {
            cur_comp = (struct component *)search_list->data;
            if (cur_comp->format_char == *format) {
              cur_addition = (cur_comp->generate)();
              /* Check for NULL component output */
              if (!cur_addition) 
                break;
              /* Insert <br> for \n to get more accurate size */
              cur_addition_copy = gaim_strdup_withhtml (cur_addition);
              free (cur_addition);
              if (strlen (cur_addition_copy) < space_left) {
                strcat (output, cur_addition_copy);
                space_left = space_left - strlen (cur_addition_copy);
              } else {
                auto_debug ("generate", "component too large, dropped");
              }

              free (cur_addition_copy);
              break;
            } else {
              search_list = search_list->next;

            }
          }
        }
   
        format++;
      }
      state = 0;
    } else {
      if (*format == '%') {
        format++;
        state = 1;
      } else if (*format == '\n') {
        if (space_left >= 4) {
          strcat (output, "<br>");
          format++;
          space_left = space_left - 4;
        } else {
          break;
        }
      } else {
        *single_char = *format++; 
        strcat (output, single_char);
        space_left--;
      }
    }
  }

  free (single_char);
  auto_debug ("generate", "message created from components");
  return output;
}

/* Change status to away */
gboolean status_away ()
{
  GList *accounts;
  int interval;
  GaimAccount *current;

  if (gaim_prefs_get_bool ("/plugins/gtk/autoprofile/queue_messages_when_away")) {
    gaim_prefs_set_string ("/gaim/gtk/conversations/im/hide_new", "away");
  } else {
    gaim_prefs_set_string ("/gaim/gtk/conversations/im/hide_new", "never");
  }

  /* Make sure at least one account is activated */
  accounts = gaim_accounts_get_all ();

  while (TRUE) {
    if (accounts == NULL) {
      gaim_notify_error (NULL, NULL,
        _("No accounts have been activated to go away"),
        _("Toggle the desired accounts under the accounts tab in "
          "AutoProfile preferences"));
      status_back ();
      return FALSE;
    }
    current = (GaimAccount *)accounts->data;
    if (gaim_account_is_connected (current) && 
        get_account_boolean (current, "enable_away"))
      break;
    accounts = accounts->next;
  }

  interval = 60 * 1000 * gaim_prefs_get_int (
    "/plugins/gtk/autoprofile/delay_away");

  /* Disable troublesome variable values */
  if (!is_away) {
    original_autoreply = strdup (gaim_prefs_get_string (
      "/core/away/auto_reply"));
    if (!strcmp (original_autoreply, "never")) {
      original_autoresponse = FALSE;
    } else {
      original_autoresponse = TRUE;
    }
  }

  gaim_prefs_set_string ("/core/away/auto_reply", "never");

  is_away = TRUE;
  if (away_timeout) 
    g_source_remove (away_timeout);
  away_timeout = g_timeout_add (interval, away_update, NULL);
  away_update (NULL); 

  return TRUE;
}

/* Change status to back */
void status_back ()
{
  GList *tmp;
  struct auto_response *p;

  gaim_prefs_set_string ("/gaim/gtk/conversations/im/hide_new", "never");

  is_away = FALSE;
  gaim_prefs_set_int ("/plugins/gtk/autoprofile/current_away", -1);

  /* Restore original preferences */
  if (original_autoreply) {
    gaim_prefs_set_string ("/core/away/auto_reply", original_autoreply);
    free (original_autoreply);
    original_autoreply = NULL;
  }
  original_autoresponse = FALSE;

  while (auto_recepients) {
    tmp = auto_recepients->next;
    p = (struct auto_response *)auto_recepients->data;
    free (p->recepient);
    free (auto_recepients->data);
    free (auto_recepients);
    auto_recepients = tmp;
  }
  
  if (away_timeout) {
    g_source_remove (away_timeout);
    away_timeout = 0;
  } 

  away_update (NULL);
}

/* Command callbacks */
static GaimCmdRet cmd_away_cb (GaimConversation *conv,
  const char *cmd, char **args, char **error, void *data)
{
  int message_num;

  if (sending_auto)
    return GAIM_CMD_RET_OK;

  message_num = gaim_prefs_get_int ("/plugins/gtk/autoprofile/default_away");
  if (message_num >= 0) {
    gaim_prefs_set_int ("/plugins/gtk/autoprofile/current_away", 
      message_num);
  } else {
    gaim_prefs_set_int ("/plugins/gtk/autoprofile/current_away", -1);
  }

  if (args && *args) {
    gaim_prefs_set_string ("/plugins/gtk/autoprofile/added_text", *args);
  } else {
    gaim_prefs_set_string ("/plugins/gtk/autoprofile/added_text", "");
  }

  if (!status_away ()) 
    return GAIM_CMD_RET_OK;

  gaim_conversation_write (conv, "AutoProfile", "Status set to away",
    GAIM_MESSAGE_SYSTEM, time (NULL));
    
  auto_debug ("cmd_away_cb", "status set to away");
  return GAIM_CMD_RET_OK;
}

static GaimCmdRet cmd_back_cb (GaimConversation *conv,
  const char *cmd, char **args, char **error, void *data)
{
  if (sending_auto)
    return GAIM_CMD_RET_OK;

  status_back ();

  gaim_conversation_write (conv, "AutoProfile", "Away status removed",
    GAIM_MESSAGE_SYSTEM, time (NULL));

  auto_debug ("cmd_back_cb", "away status removed");
  return GAIM_CMD_RET_OK;
}

/* Profiles: Update every so often */
gboolean profile_update (gpointer data)
{
  GaimAccount *account;
  gboolean profile_bool;
  GList *gaim_accounts;

  /* Create the text (due to high speed, random-system will not differentiate
   * between multiple calls to generate() and same text will result across all
   * profiles */
  char *text, *initial_text;
  int profile_num;
  GList *cur_node, *start_node;

  profile_num = gaim_prefs_get_int (
    "/plugins/gtk/autoprofile/default_profile");
 
  if (profile_num < 0) {
    auto_debug ("profile", "no profile has been set");
    return TRUE;
  }

  cur_node = gaim_prefs_get_string_list (
    "/plugins/gtk/autoprofile/message_texts");
  start_node = cur_node;
  while (profile_num-- != 0) {
    cur_node = cur_node->next;
  }
   
  initial_text = (char *)cur_node->data;

  if (initial_text != NULL && *initial_text != '\0') {
    text = autoprofile_generate (initial_text);
    if (*text == '\0') {
      free (text);
      text = strdup (" ");
    }
  } else {
    text = strdup (" ");
  }
  free_string_list (start_node);

  /* Get all accounts and search through each */
  auto_debug ("profile", "updating profiles");

  
  gaim_accounts = gaim_accounts_get_all ();
  while (gaim_accounts) {
    account = (GaimAccount *)gaim_accounts->data;
    profile_bool = get_account_boolean (account, _("enable_profile"));

    /* Check to see if update option set on account */
    if (profile_bool) {
      gaim_account_set_user_info (account, text);
      
      if (gaim_account_is_connected (account)) {
        GaimConnection *gc = gaim_account_get_connection (account);
        serv_set_info (gc, text);
      } else {
        auto_debug ("profile", "account not online, skipping");
      }
    } 
    gaim_accounts = gaim_accounts->next;
  }
  free (text);

  return TRUE;
}

/* Away messages: Update every so often */
gboolean away_update (gpointer data)
{
  GaimAccount *account;
  gboolean away_bool;
  gboolean activated = FALSE;
 
  char *text, *initial_text;
  int away_num;
  GList *start_node, *cur_node;

  /* Get all accounts and search through each */
  GList *gaim_accounts = gaim_accounts_get_all_active ();

  if (is_away) {

    away_num = gaim_prefs_get_int ("/plugins/gtk/autoprofile/current_away");
 
    if (away_num < 0) {
      auto_debug ("away", "no away message chosen");
      return TRUE;
    }

    cur_node = gaim_prefs_get_string_list (
      "/plugins/gtk/autoprofile/message_texts");
    start_node = cur_node;
    while (away_num-- != 0) {
      cur_node = cur_node->next;
    }
   
    initial_text = (char *)cur_node->data;
    
    if (initial_text != NULL && *initial_text != '\0') {
      text = autoprofile_generate (initial_text);
      if (*text == '\0') {
        free (text);
        text = strdup (" ");
      }
    } else {
      text = strdup (" ");
    }
   
    free_string_list (start_node);
  } else {
    text = NULL;
  }

  auto_debug ("away", "updating away messages");

  while (gaim_accounts) {
  
    account = (GaimAccount *)gaim_accounts->data;
    away_bool = get_account_boolean (account, _("enable_away"));

    /* Check to see if update option set on account */
    if (away_bool) {
        if (text != NULL) {
          GaimSavedStatus *saved_status = gaim_savedstatus_new (
            NULL, GAIM_STATUS_AWAY);
          gaim_savedstatus_set_message (saved_status, text);
          gaim_savedstatus_activate_for_account (saved_status, account);
        } else {
          GaimSavedStatus *saved_status = gaim_savedstatus_new (
            NULL, GAIM_STATUS_AVAILABLE);
          gaim_savedstatus_activate_for_account (saved_status, account);
        }

            activated = TRUE;
    } 
    gaim_accounts = gaim_accounts->next;
  }

  if (is_away && activated) {
    gtk_away_msgs_create (text);
  } else {
    gtk_away_msgs_destroy ();
  }

  free (text);

  return TRUE;
}

/*--------------------------------------------------------------------------
 * LAST CALL                                               
 *------------------------------------------------------------------------*/

static void init_plugin (GaimPlugin *plugin) 
{ 
  struct component *comp;
  GList *comp_list, *start_list;
  
  GList *title, *message;
  title = (GList *)malloc(sizeof(GList));
  message = (GList *)malloc(sizeof(GList));
  title->next = NULL;
  message->next = NULL;
  title->data = _("Not boring at all default");
  message->data = _("Get Autoprofile for Gaim at <a href=\""
    "http://hkn.eecs.berkeley.edu/~casey/autoprofile/\">"
    "hkn.eecs.berkeley.edu/~casey/autoprofile/</a><br><br>%s<br>");

  gaim_prefs_add_none ("/plugins/gtk");
  gaim_prefs_add_none ("/plugins/gtk/autoprofile");
  gaim_prefs_add_none ("/plugins/gtk/autoprofile/components");
  gaim_prefs_add_none ("/plugins/gtk/autoprofile/accounts");

  gaim_prefs_add_string_list ("/plugins/gtk/autoprofile/message_titles", 
    title);
  gaim_prefs_add_string_list ("/plugins/gtk/autoprofile/message_texts", 
    message);
  gaim_prefs_add_int ("/plugins/gtk/autoprofile/default_profile", 0);
  gaim_prefs_add_int ("/plugins/gtk/autoprofile/default_away", 0);
  gaim_prefs_add_int ("/plugins/gtk/autoprofile/current_away", -1);
  gaim_prefs_add_string ("/plugins/gtk/autoprofile/added_text", "");

  gaim_prefs_add_string ("/plugins/gtk/autoprofile/text_respond",
    "Say the magic word if you want me to talk more!");
  gaim_prefs_add_string ("/plugins/gtk/autoprofile/text_trigger", "please");
  gaim_prefs_add_int ("/plugins/gtk/autoprofile/delay_respond", 5);
  gaim_prefs_add_int ("/plugins/gtk/autoprofile/delay_profile", 5);
  gaim_prefs_add_int ("/plugins/gtk/autoprofile/delay_away", 5);
  gaim_prefs_add_bool ("/plugins/gtk/autoprofile/use_trigger", TRUE);

  gaim_prefs_add_int ("/plugins/gtk/autoprofile/tab_number", 0);

  gaim_prefs_add_string_list("/plugins/gtk/autoprofile/accounts/enable_away",
    NULL);
  gaim_prefs_add_string_list("/plugins/gtk/autoprofile/accounts/enable_profile", 
    NULL);

  /* Initialize preferences for each component */
  comp_list = get_components ();
  start_list = comp_list;
  while (comp_list) {
    comp = comp_list->data;
    if (comp->init_pref)        // Make sure init_pref not null
      (comp->init_pref) ();
    comp_list = comp_list->next;
  }

  /* Cleanup */
  g_list_free (start_list);
  free (title);
  free (message);
}

GAIM_INIT_PLUGIN (autoprofile, init_plugin, info)


Generated by  Doxygen 1.6.0   Back to index