Using libcurl in libpurple edit

http://pidgin.im/pipermail/devel/2007-November/003967.html
Vishal Rao vishalrao at gmail.com
Tue Nov 6 01:08:27 EST 2007


Hello,

I'm writing a libpurple plugin (cross-platform Windows and Linux/Unix)
and I need to do an FTP upload via code. Can you recommend some
quick/easy library to do an FTP upload which also compiles/works on
Windows and Linux?

Thanks,
Vishal Rao

--
"Thou shalt not follow the null pointer for at it's end madness and chaos lie."

http://pidgin.im/pipermail/devel/2007-November/003968.html
Casey Harkins caseyharkins at gmail.com
Tue Nov 6 01:41:18 EST 2007


libcurl perhaps? Never used it, so I'm not sure about quick/easy, but it
does support most platforms (including Windows, Linux and most flavors
of Unix) and multiple protocols.

http://curl.haxx.se/libcurl/

-casey

http://pidgin.im/pipermail/devel/2007-November/003971.html
Vishal Rao vishalrao at gmail.com
Tue Nov 6 08:29:02 EST 2007


thanks, i've started to use the libcurl which comes with cygwin and my
libpurple plugin compiles under windows/cygwin/mingw.

problem is, when i include curl and call its init and cleanup
functions in the plugin code, the plugin no longer shows up in
pidgin's plugin list to load! the plugin works again when i comment
out the curl code... im statically linking curl and my plugin dll size
goes up from about 100k to 500k.

any ideas as to why this is happening and how to get it to work?

--
"Thou shalt not follow the null pointer for at it's end madness and chaos lie."

http://pidgin.im/pipermail/devel/2007-November/003972.html
Daniel Atallah daniel.atallah at gmail.com
Tue Nov 6 09:10:12 EST 2007


The problem is likely that you're using the cygwin-dependent libcurl.
The reason that your plugin no longer appears is that it probably
can't find cygwin1.dll, which libcurl needs. Using the MS dependency
checker application on your plugin dll will verify this. Pidgin (on
Windows) is a native win32 application, so you should use a natively
compiled libcurl instead and avoid having these problems.

-D

http://pidgin.im/pipermail/devel/2007-November/003977.html
Vishal Rao vishalrao at gmail.com
Tue Nov 6 11:19:38 EST 2007


Yup, it did seem to have a dependency on cygwin. So I built libcurl
myself and rebuilt the plugin. Still see the same problem. Dependency
walker shows dependency on libpurple.dll and dwmapi.dll which is the
same as the working plugin without curl.

If I comment out the calls to curl's init and cleanup, the plugin
loads. I moved the calls to other functions and not the plugin's
init/load, still the plugin does not load if I include curl calls...

--
"Thou shalt not follow the null pointer for at it's end madness and chaos lie."

http://pidgin.im/pipermail/devel/2007-November/003980.html
Vishal Rao vishalrao at gmail.com
Tue Nov 6 11:36:42 EST 2007


Doh, nevermind. I just needed to place libcurl.dll in Pidgin's folder
not in the plugins subfolder. Static linking is giving me linker
errors so I'm using the curl dll.... thanks!

--
"Thou shalt not follow the null pointer for at it's end madness and chaos lie."

http://pidgin.im/pipermail/devel/2007-November/003988.html
Vishal Rao vishalrao at gmail.com
Wed Nov 7 00:46:56 EST 2007

FYI I've attached my libpurple plugin code for any comments on it.
It's supposed to be a "webaware" plugin similar to miranda-im's
webaware plugin which uploads your status so you can display it on
your webpage.

I'm writing it mostly for myself because I didn't find an existing
replacement plugin for Pidgin which I'm starting to use on Linux and
Windows... I hope there doesn't already exist a similar plugin for
Pidgin which I could just use?

--
"Thou shalt not follow the null pointer for at it's end madness and chaos lie."
-------------- next part --------------
A non-text attachment was scrubbed...
Name: webaware.c
Type: text/x-csrc
Size: 11254 bytes
Desc: not available
Url : http://pidgin.im/pipermail/devel/attachments/20071107/eeedb93a/attachment-0001.c

attachment-0001.c:
/*
 * WebAware (version 0.0.3) libpurple plugin.
 *
 * Copyright (C) 2007 Vishal Rao <vishal.rao@lahsiv.net>
 *
 * 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.
 * http://www.gnu.org/copyleft/gpl.html
 */

/** 
 * This is a libpurple plugin which places your IM status on FTP or local directories.
 * It is useful for displaying your status on your website.
 */

/*
Changelog:

0.0.3 (Nov 6th, 2007) :
Now using libcurl and glib threads/mutexes for the (hardcoded for me) FTP upload.

0.0.2 (Oct 9th, 2007) :
Now just listens to account-status-changed signals.

0.0.1 (Oct 9th, 2007) :
First cut based on helloworld.c hardcoded only for MSN, Yahoo and GoogleTalk.
Can only listen to signed-on and signed-off signals.
Does not store status anywhere in this version.
*/

/*
TODO :

1. Actually place status on multiple FTP and local dirs.
2. Handle all available protocols not just MSN, Yahoo and GoogleTalk.
3. Use Pref API to load/store config along with UI.
*/

#define PURPLE_PLUGINS

#include <glib.h>

#ifndef G_GNUC_NULL_TERMINATED
  #if __GNUC__ >= 4
    #define G_GNUC_NULL_TERMINATED __attribute__((__sentinel__))
  #else
    #define G_GNUC_NULL_TERMINATED
  #endif /* __GNUC__ >= 4 */
#endif /* G_GNUC_NULL_TERMINATED */

#include "plugin.h"
#include "version.h"
#include "request.h"
#include "notify.h"
#include "connection.h"
#include "signals.h"
#include "util.h"

#include <curl/curl.h>

CURL * g_curl = NULL;

char g_curldata[500] = {0};
int g_curldatalen = 0;

GMutex * g_statusmutex = NULL;
GMutex * g_threadmutex = NULL;

int g_threadstarted = 0;
GThread * g_thread = NULL;
GError * g_error = NULL;

PurplePlugin * g_plugin = NULL;

/*
 * Connection protocol_id list:
 * MSN is "prpl-msn"
 * Yahoo is "prpl-yahoo"
 * GTalk is "prpl-jabber"
 */

PurpleConnection * g_msn = NULL;
PurpleConnection * g_yahoo = NULL;
PurpleConnection * g_gtalk = NULL;

gulong g_statusid = 0;

char g_msnstatus[100] = {0};
char g_yahoostatus[100] = {0};
char g_gtalkstatus[100] = {0};

static void
check_connection(PurpleConnection * conn)
{
  const char * msn = NULL;
  const char * yahoo = NULL;
  const char * gtalk = NULL;

  if (!g_msn)
  {
    msn = purple_strcasestr(conn->account->protocol_id, "prpl-msn");

    if (msn)
    {
      g_msn = conn;
      return;
    }
  }

  if (!g_yahoo)
  {
    yahoo = purple_strcasestr(conn->account->protocol_id, "prpl-yahoo");

    if (yahoo)
    {
      g_yahoo = conn;
      return;
    }
  }

  if (!g_gtalk)
  {
    gtalk = purple_strcasestr(conn->account->protocol_id, "prpl-jabber");

    if (gtalk)
    {
      g_gtalk = conn;
      return;
    }
  }
}

static void
update_status(PurpleConnection * conn, char * gstatus)
{
  GList * statuses = NULL;
  GList * statusitem = NULL;
  PurpleStatus * status = NULL;
  PurpleStatusType * statustype = NULL;
  PurpleStatusPrimitive statusprimitive = PURPLE_STATUS_UNSET;

  g_strlcpy(gstatus, "Unknown", 99);
  
  statuses = purple_presence_get_statuses(conn->account->presence);
  
  statusitem = g_list_first(statuses);

  g_mutex_lock(g_statusmutex);
  
  while (statusitem)
  {
    status = (PurpleStatus *) statusitem->data;
    
    if (purple_status_is_active(status))
    {
      statustype = purple_status_get_type(status);
      
      statusprimitive = purple_status_type_get_primitive(statustype);

/*      
      purple_notify_message(g_plugin, PURPLE_NOTIFY_MSG_INFO, conn->account->protocol_id,
        purple_primitive_get_name_from_type(statusprimitive), NULL, NULL, NULL);
        */
      
      g_strlcpy(gstatus, purple_primitive_get_name_from_type(statusprimitive), 99);
    }
    
    statusitem = g_list_next(statusitem);
  }
  
  g_mutex_unlock(g_statusmutex);
}

// curlreader is the "data provider" for the libcurl ftp upload
static size_t
curlreader(void *ptr, size_t size, size_t nmemb, void *stream)
{
  static int total = 0;
  int current = 0;
  
  while (total < g_curldatalen && current < size*nmemb)
  {
    *(((char*)ptr)+current) = *(((char*)stream)+total);
    
    total++;
    current++;
  }
  
  if (current == 0) total = 0;
  
  return current;
}

// uploader is the thread which waits 10s before performing the upload
static gpointer
uploader(gpointer data)
{
  CURLcode result;
  //GTimer * timer = NULL;
  
  //sleep(10000); // wait 10 seconds before uploading status...
  
  /*
  timer = g_timer_new();
  g_timer_start(timer);
  while(g_timer_elapsed(timer, NULL) < 10.0);
  g_timer_destroy(timer);
  */
  
  g_usleep(10000000); // wait 10 seconds before uploading status...
  
  // currently uploading miranda-im's webaware_data.js file format!
  
  g_mutex_lock(g_statusmutex);
  strcpy(g_curldata, "initArray(4);\n");
  strcat(g_curldata, "addData(new Array(\"CSTIME\", 2007, 11, 06, 15, 00, 00));\n");
  strcat(g_curldata, "addData(new Array(\"STATUS\", \"JABBER\", 40071, \"");
  strcat(g_curldata, g_gtalkstatus);
  strcat(g_curldata, "\", \"Nope, not here.\"));\n");
  strcat(g_curldata, "addData(new Array(\"STATUS\", \"MSN\", 40071, \"");
  strcat(g_curldata, g_msnstatus);
  strcat(g_curldata, "\", \"Nope, not here.\"));\n");
  strcat(g_curldata, "addData(new Array(\"STATUS\", \"YAHOO\", 40071, \"");
  strcat(g_curldata, g_yahoostatus);
  strcat(g_curldata, "\", \"Nope, not here.\"));\n");  
  g_mutex_unlock(g_statusmutex);
  g_curldatalen = strlen(g_curldata);
  
  g_curl = curl_easy_init();
  // upload to ftp
  if (g_curl)
  {
    curl_easy_setopt(g_curl, CURLOPT_UPLOAD, 1);
    curl_easy_setopt(g_curl, CURLOPT_URL, "ftp://user:pass@site.tld/folder/file");
    curl_easy_setopt(g_curl, CURLOPT_READDATA, g_curldata);
    curl_easy_setopt(g_curl, CURLOPT_READFUNCTION, &curlreader);
    result = curl_easy_perform(g_curl);
    curl_easy_cleanup(g_curl);
  }  
  
  g_mutex_lock(g_threadmutex);
  g_threadstarted = 0;
  g_mutex_unlock(g_threadmutex);
  
  return NULL;
}

static void
save_status(const char * gstatus)
{
  g_mutex_lock(g_threadmutex);
  
  if (g_threadstarted == 0)
  {
    g_thread = g_thread_create(&uploader, NULL, FALSE, &g_error);
    g_threadstarted = 1;
  }
  
  g_mutex_unlock(g_threadmutex);
}

static void
save_status2(const char * gstatus)
{
  CURLcode result;
  
  // TODO : Read prefs and save status to FTP and/or local dir.
  /*
  purple_notify_message(g_plugin, PURPLE_NOTIFY_MSG_INFO, "Save Status",
    gstatus, NULL, NULL, NULL);  
  */
  g_curl = curl_easy_init();
  // upload to ftp
  if (g_curl)
  {
    curl_easy_setopt(g_curl, CURLOPT_UPLOAD, 1);
    curl_easy_setopt(g_curl,CURLOPT_URL, "ftp://user:pass@site.tld/folder/file");
    curl_easy_setopt(g_curl, CURLOPT_READDATA, g_curldata);
    curl_easy_setopt(g_curl, CURLOPT_READFUNCTION, &curlreader);
    result = curl_easy_perform(g_curl);
    curl_easy_cleanup(g_curl);
  }
}

static void
update_and_save_status(PurpleConnection * conn)
{
  const char * msn = NULL;
  const char * yahoo = NULL;
  const char * gtalk = NULL;

  msn = purple_strcasestr(conn->account->protocol_id, "prpl-msn");
  yahoo = purple_strcasestr(conn->account->protocol_id, "prpl-yahoo");
  gtalk = purple_strcasestr(conn->account->protocol_id, "prpl-jabber");

  if (msn)
  {
    update_status(g_msn, g_msnstatus);
    save_status(g_msnstatus);
  }
  else if (yahoo)
  {
    update_status(g_yahoo, g_yahoostatus);
    save_status(g_yahoostatus);
  }
  else if (gtalk)
  {
    update_status(g_gtalk, g_gtalkstatus);
    save_status(g_gtalkstatus);
  }
}

static void
account_status_changed(PurpleAccount *account, PurpleStatus *old, PurpleStatus *new, gpointer data)
{
  PurpleConnection * conn = NULL;
  /*
  purple_debug_misc("signals test", "account-status-changed (%s, %s, %s)\n",
          purple_account_get_username(account),
          purple_status_get_name(old),
          purple_status_get_name(new));
  */
  
  /*
  char msg[100] = {0};
  
  g_snprintf(msg, 99, "account: %s, old: %s, new: %s", purple_account_get_protocol_name(account),
    purple_status_get_name(old), purple_status_get_name(new));
  
  purple_notify_message(g_plugin, PURPLE_NOTIFY_MSG_INFO, "Account Status Changed",
    msg, NULL, NULL, NULL);  
  */
  
  conn = purple_account_get_connection(account);
  check_connection(conn);
  update_and_save_status(conn);  
}

static gboolean
plugin_load(PurplePlugin * plugin)
{
  /*
  purple_notify_message(plugin, PURPLE_NOTIFY_MSG_INFO, "WebAware Load",
    "plugin_load() called", NULL, NULL, NULL);
  */

  /*
  g_signonid = purple_signal_connect(purple_connections_get_handle(), "signed-on", g_plugin,
    PURPLE_CALLBACK(signed_on_cb), NULL);

  g_signoffid = purple_signal_connect(purple_connections_get_handle(), "signed-off", g_plugin,
    PURPLE_CALLBACK(signed_off_cb), NULL);
  */

  /*
  g_statusid = purple_signal_connect(purple_connections_get_handle(), "savedstatus-changed", g_plugin,
    PURPLE_CALLBACK(savedstatus_changed_cb), NULL);
  */

  g_statusid = purple_signal_connect(purple_accounts_get_handle(), "account-status-changed", g_plugin,
    PURPLE_CALLBACK(account_status_changed), NULL);

  g_statusmutex = g_mutex_new();
  g_threadmutex = g_mutex_new();
  
  return TRUE;
}

static gboolean
plugin_unload(PurplePlugin * plugin)
{
  /*
  purple_notify_message(plugin, PURPLE_NOTIFY_MSG_INFO, "WebAware UnLoad",
    "plugin_unload() called", NULL, NULL, NULL);
  */

  purple_signals_disconnect_by_handle(g_plugin);

  g_mutex_free(g_statusmutex);
  g_mutex_free(g_threadmutex);
  
  return TRUE;
}

static void
plugin_destroy(PurplePlugin * plugin)
{
  /*
  purple_notify_message(plugin, PURPLE_NOTIFY_MSG_INFO, "WebAware Destroy",
    "plugin_destroy() called", NULL, NULL, NULL);
  */

  purple_signals_uninit();
  purple_connections_uninit();
}

static PurplePluginInfo
info =
{
    PURPLE_PLUGIN_MAGIC,
    PURPLE_MAJOR_VERSION,
    PURPLE_MINOR_VERSION,
    PURPLE_PLUGIN_STANDARD,
    NULL,
    0,
    NULL,
    PURPLE_PRIORITY_DEFAULT,

    "core-vishalrao-webaware",
    "WebAware",
    "0.0.3",

    "Web Awareness Plugin",
    "The WebAware plugin places your status information on FTP or local directories."
    " It is useful for being able to display your status on your webpage.",
    "Vishal Rao <vishal.rao@lahsiv.net>",
    "http://lahsiv.net/",

    plugin_load,
    plugin_unload,
    plugin_destroy,

    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL 
};

static void
init_plugin(PurplePlugin * plugin)
{
  g_plugin = plugin;
  purple_signals_init();
  purple_connections_init();
  
  //Note: purple_accounts_init() seems to cause Pidgin to not sign on!
  //purple_accounts_init();
}

PURPLE_INIT_PLUGIN(webaware, init_plugin, info);

// --------- The End ---------

2 comments:

RealMenRealTopics said...

Hello! Let me first state that you definitely succeeded in building a magnificent domain. In addition to that I want to ask you a question that is very important to me. Do you have plans to write as a professional or having a blog is just a kind of hobby?

89038102 said...

It's just a kind of hobby. why did you leave the comment for this specific post? it was just a copy&paste post.

Post a Comment

Newer -> <- Older