libemail.c 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924
  1. /*
  2. * Email plugin for libpurple
  3. * Copyright (C) 2017 Alyssa Rosenzweig
  4. * Copyright (C) 2016 Eion Robb
  5. *
  6. * This program is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <string.h>
  22. #ifdef __GNUC__
  23. #include <unistd.h>
  24. #endif
  25. #include <errno.h>
  26. #ifdef ENABLE_NLS
  27. # define GETTEXT_PACKAGE "purple-email"
  28. # include <glib/gi18n-lib.h>
  29. # ifdef _WIN32
  30. # ifdef LOCALEDIR
  31. # unset LOCALEDIR
  32. # endif
  33. # define LOCALEDIR wpurple_locale_dir()
  34. # endif
  35. #else
  36. # define _(a) (a)
  37. # define N_(a) (a)
  38. #endif
  39. #include <glib.h>
  40. #include "purplecompat.h"
  41. #include "purple2compat/purple-socket.h"
  42. #define EMAIL_PLUGIN_ID "prpl-alyssa-mail"
  43. #ifndef EMAIL_PLUGIN_VERSION
  44. #define EMAIL_PLUGIN_VERSION "0.1"
  45. #endif
  46. #define EMAIL_PLUGIN_WEBSITE "https://notabug.org/alyssa/purple-email"
  47. #define SMTP_PORT 587
  48. #define SMTPS_PORT 465
  49. #define IMAP_PORT 143
  50. #define IMAPS_PORT 993
  51. enum IMAPState {
  52. IMAP_ANONYMOUS = 0,
  53. IMAP_AUTHENTICATING,
  54. IMAP_AUTHENTICATED,
  55. IMAP_SELECT,
  56. IMAP_IDLING,
  57. IMAP_FETCHING
  58. };
  59. typedef struct {
  60. PurpleAccount *account;
  61. PurpleConnection *pc;
  62. gchar *domain;
  63. PurpleSocket *imap;
  64. enum IMAPState imap_state;
  65. int imap_id;
  66. } EmailAccount;
  67. static gboolean
  68. plugin_load(PurplePlugin *plugin, GError **error)
  69. {
  70. return TRUE;
  71. }
  72. static gboolean
  73. plugin_unload(PurplePlugin *plugin, GError **error)
  74. {
  75. purple_signals_disconnect_by_handle(plugin);
  76. return TRUE;
  77. }
  78. /* Purple2 Plugin Load Functions */
  79. #if !PURPLE_VERSION_CHECK(3, 0, 0)
  80. static gboolean
  81. libpurple2_plugin_load(PurplePlugin *plugin)
  82. {
  83. return plugin_load(plugin, NULL);
  84. }
  85. static gboolean
  86. libpurple2_plugin_unload(PurplePlugin *plugin)
  87. {
  88. return plugin_unload(plugin, NULL);
  89. }
  90. static GHashTable *
  91. email_get_account_text_table(PurpleAccount *unused)
  92. {
  93. GHashTable *table;
  94. table = g_hash_table_new(g_str_hash, g_str_equal);
  95. g_hash_table_insert(table, "login_label", (gpointer) _("Email address..."));
  96. return table;
  97. }
  98. static GList *
  99. email_chat_info(PurpleConnection *pc)
  100. {
  101. GList *m = NULL;
  102. PurpleProtocolChatEntry *pce;
  103. pce = g_new0(PurpleProtocolChatEntry, 1);
  104. pce->label = _("ID");
  105. pce->identifier = "id";
  106. m = g_list_append(m, pce);
  107. pce = g_new0(PurpleProtocolChatEntry, 1);
  108. pce->label = _("Name");
  109. pce->identifier = "name";
  110. m = g_list_append(m, pce);
  111. return m;
  112. }
  113. static GHashTable *
  114. email_chat_info_defaults(PurpleConnection *pc, const char *chatname)
  115. {
  116. GHashTable *defaults = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
  117. return defaults;
  118. }
  119. #define COMPONENT_USER 0
  120. #define COMPONENT_DOMAIN 1
  121. gchar *
  122. email_address_component(const gchar *address, int component)
  123. {
  124. gchar **components = g_strsplit(address, "@", 2);
  125. gchar *out = g_strdup(components[component]);
  126. g_strfreev(components);
  127. return out;
  128. }
  129. void
  130. email_imap_login(EmailAccount *acc)
  131. {
  132. gchar *username = email_address_component(purple_account_get_username(acc->account), COMPONENT_USER);
  133. gchar *q = g_strdup_printf("a%d LOGIN %s %s\r\n", acc->imap_id++, username, purple_account_get_password(acc->account));
  134. purple_socket_write(acc->imap, (guchar *) q, strlen(q));
  135. g_free(q);
  136. g_free(username);
  137. acc->imap_state = IMAP_AUTHENTICATING;
  138. }
  139. void email_imap_select(EmailAccount *acc, const char *inbox)
  140. {
  141. gchar *q = g_strdup_printf("a%d SELECT %s\r\n", acc->imap_id++, inbox);
  142. purple_socket_write(acc->imap, (guchar *) q, strlen(q));
  143. g_free(q);
  144. }
  145. void email_imap_idle(EmailAccount *acc)
  146. {
  147. gchar *q = g_strdup_printf("a%d IDLE\r\n", acc->imap_id++);
  148. purple_socket_write(acc->imap, (guchar *) q, strlen(q));
  149. g_free(q);
  150. }
  151. void email_imap_fetch(EmailAccount *acc, const gchar *msg)
  152. {
  153. gchar *q = g_strdup_printf("a%d FETCH %s (BODY[HEADER] BODY[TEXT])\r\n", acc->imap_id++, msg);
  154. purple_socket_write(acc->imap, (guchar *) q, strlen(q));
  155. g_free(q);
  156. }
  157. typedef struct {
  158. gchar *body;
  159. gchar *sender;
  160. } IncomingMessage;
  161. gchar *
  162. email_parse_section(gchar *fetch, const gchar *section) {
  163. gchar *start = g_strrstr(fetch, section);
  164. if(!start) {
  165. return NULL;
  166. }
  167. gchar *end;
  168. int len = g_ascii_strtoull(start + strlen(section) + 2, &end, 10);
  169. return g_strndup(end + 3, len - 2);
  170. }
  171. gboolean
  172. email_parse_fetch(gchar *fetch, IncomingMessage *msg)
  173. {
  174. msg->body = email_parse_section(fetch, "BODY[TEXT]");
  175. if (!msg->body) {
  176. printf("No body parsed\n");
  177. return FALSE;
  178. }
  179. gchar *headers = email_parse_section(fetch, "BODY[HEADER]");
  180. if (!headers) {
  181. printf("No headers\n");
  182. return FALSE;
  183. }
  184. GRegex *from_regex = g_regex_new("(^|\r|\n|\r\n)From: (.*)\r\n", 0, 0, NULL);
  185. GRegex *brackets = g_regex_new("<(.*)>", 0, 0, NULL);
  186. GMatchInfo *match;
  187. if (!g_regex_match(from_regex, headers, 0, &match)) {
  188. printf("No from\n");
  189. return FALSE;
  190. }
  191. gchar *from = g_match_info_fetch(match, 2);
  192. GMatchInfo *fm;
  193. if (g_regex_match(brackets, from, 0, &fm)) {
  194. g_free(from);
  195. from = g_match_info_fetch(fm, 1);
  196. g_match_info_free(fm);
  197. }
  198. msg->sender = from;
  199. g_match_info_free(match);
  200. g_regex_unref(from_regex);
  201. g_regex_unref(brackets);
  202. return TRUE;
  203. }
  204. void
  205. email_got_imap_line(EmailAccount *acc, gchar *buffer) {
  206. if (!buffer || !strlen(buffer))
  207. return;
  208. printf("Line: %s\n", buffer);
  209. if (acc->imap_state == IMAP_ANONYMOUS) {
  210. /* Handshake received, login */
  211. email_imap_login(acc);
  212. } else if (acc->imap_state == IMAP_AUTHENTICATING) {
  213. /* Login response received */
  214. gchar **response = g_strsplit(buffer, " ", 3);
  215. gchar *code = response[1];
  216. if (!code) {
  217. printf("Uh oh\n");
  218. printf("%s\n", buffer);
  219. return;
  220. }
  221. if (purple_strequal(code, "OK")) {
  222. purple_connection_set_state(acc->pc, PURPLE_CONNECTION_CONNECTED);
  223. acc->imap_state = IMAP_SELECT;
  224. email_imap_select(acc, "Inbox");
  225. } else if (purple_strequal(code, "BAD")) {
  226. purple_connection_error(acc->pc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Bad username/password"));
  227. acc->imap_state = IMAP_ANONYMOUS;
  228. } else {
  229. /* RIP the spec */
  230. }
  231. g_strfreev(response);
  232. } else if (acc->imap_state == IMAP_SELECT) {
  233. /* Mailbox selected TODO vet response */
  234. if (buffer[0] == '*') {
  235. puts(buffer);
  236. } else if (buffer[0] == 'a') {
  237. /* TODO: Is ok? */
  238. email_imap_idle(acc);
  239. acc->imap_state = IMAP_IDLING;
  240. }
  241. } else if (acc->imap_state == IMAP_IDLING) {
  242. if (buffer[0] == '*') {
  243. gchar **words = g_strsplit(buffer, " ", 3);
  244. if (words[0] && words[1] && words[2] && purple_strequal(words[2], "EXISTS")) {
  245. gchar *new_message = words[1];
  246. /* End IDLE to fetch message */
  247. gchar *q = "DONE\r\n";
  248. purple_socket_write(acc->imap, (guchar *) q, strlen(q));
  249. /* and fetch it */
  250. email_imap_fetch(acc, new_message);
  251. acc->imap_state = IMAP_FETCHING;
  252. }
  253. g_strfreev(words);
  254. }
  255. }
  256. }
  257. void
  258. email_got_imap(gpointer user_data, gint fd, PurpleInputCondition condition)
  259. {
  260. EmailAccount *acc = user_data;
  261. PurpleSocket *imap = acc->imap;
  262. guchar buffer[65536];
  263. gsize sz = purple_socket_read(imap, buffer, sizeof(buffer) - 1);
  264. if (sz > 0) {
  265. buffer[sz] = 0;
  266. if (acc->imap_state == IMAP_FETCHING) {
  267. /* TODO: Parse message correctly :p */
  268. IncomingMessage msg;
  269. if (buffer[0] == '*') {
  270. if (email_parse_fetch((gchar *) buffer, &msg)) {
  271. purple_serv_got_im(acc->pc, msg.sender, msg.body, PURPLE_MESSAGE_RECV, time(NULL));
  272. printf("Incoming: %s from %s\n", msg.body, msg.sender);
  273. email_imap_idle(acc);
  274. acc->imap_state = IMAP_IDLING;
  275. } else {
  276. printf("Ruh-roh, couldn't parse %s", (gchar *) buffer);
  277. }
  278. }
  279. } else {
  280. gchar **lines = g_strsplit((gchar *) buffer, "\r\n", 0);
  281. for (int i = 0; lines[i]; ++i) {
  282. email_got_imap_line(acc, lines[i]);
  283. }
  284. g_strfreev(lines);
  285. }
  286. }
  287. }
  288. void
  289. email_imap_connected(PurpleSocket *ps, const gchar *error, gpointer user_data)
  290. {
  291. if (error) {
  292. printf("Error connecting %s\n", error);
  293. return;
  294. }
  295. printf("Connected\n");
  296. purple_socket_watch(ps, PURPLE_INPUT_READ, email_got_imap, user_data);
  297. }
  298. void
  299. email_login(PurpleAccount *account)
  300. {
  301. EmailAccount *da;
  302. PurpleConnection *pc = purple_account_get_connection(account);
  303. PurpleConnectionFlags pc_flags;
  304. if (!strchr(purple_account_get_username(account), '@')) {
  305. purple_connection_error(pc, PURPLE_CONNECTION_ERROR_INVALID_USERNAME, _("Username needs to be an email address"));
  306. return;
  307. }
  308. pc_flags = purple_connection_get_flags(pc);
  309. pc_flags |= PURPLE_CONNECTION_FLAG_HTML;
  310. purple_connection_set_flags(pc, pc_flags);
  311. da = g_new0(EmailAccount, 1);
  312. purple_connection_set_protocol_data(pc, da);
  313. da->account = account;
  314. da->pc = pc;
  315. purple_connection_set_state(pc, PURPLE_CONNECTION_CONNECTING);
  316. /* Do IMAP login */
  317. printf("Connecting to IMAP...\n");
  318. da->imap_state = IMAP_ANONYMOUS;
  319. da->domain = email_address_component(purple_account_get_username(da->account), COMPONENT_DOMAIN);
  320. da->imap = purple_socket_new(pc);
  321. purple_socket_set_tls(da->imap, TRUE);
  322. purple_socket_set_host(da->imap, purple_account_get_string(da->account, "imap_server", da->domain));
  323. purple_socket_set_port(da->imap, IMAPS_PORT);
  324. purple_socket_connect(da->imap, email_imap_connected, da);
  325. }
  326. static GList *
  327. email_status_types(PurpleAccount *account)
  328. {
  329. GList *types = NULL;
  330. PurpleStatusType *status;
  331. status = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, "set-online", _("Online"), TRUE, TRUE, FALSE);
  332. types = g_list_append(types, status);
  333. status = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, "set-offline", _("Offline"), TRUE, TRUE, FALSE);
  334. types = g_list_append(types, status);
  335. return types;
  336. }
  337. static void
  338. email_close(PurpleConnection *pc)
  339. {
  340. EmailAccount *da = purple_connection_get_protocol_data(pc);
  341. g_return_if_fail(da != NULL);
  342. g_free(da);
  343. }
  344. /* Hacky SMTP implementation ¯\_(ツ)_/¯ */
  345. typedef enum {
  346. SMTP_ANONYMOUS = 0,
  347. SMTP_HELLO,
  348. SMTP_AUTH,
  349. SMTP_FROM,
  350. SMTP_RCPT,
  351. SMTP_DATA,
  352. SMTP_XFER,
  353. SMTP_QUIT
  354. } SMTPState;
  355. typedef struct {
  356. EmailAccount *account;
  357. SMTPState state;
  358. PurpleSocket *socket;
  359. /* email addresses + aliases */
  360. gchar *sender;
  361. gchar *sender_alias;
  362. gchar *recipient;
  363. gchar *recipient_alias;
  364. gchar *subject;
  365. gchar *body;
  366. } OutgoingMessage;
  367. void
  368. email_got_smtp(gpointer user_data, gint fd, PurpleInputCondition condition)
  369. {
  370. OutgoingMessage *msg = user_data;
  371. guchar buffer[2048];
  372. gsize sz = purple_socket_read(msg->socket, buffer, sizeof(buffer) - 1);
  373. if (sz > 0) {
  374. if (msg->state == SMTP_ANONYMOUS) {
  375. if (buffer[0] == '2' && buffer[1] == '2' && buffer[2] == '0') {
  376. /* Greetings, server! */
  377. gchar *ehlo = g_strdup_printf("EHLO %s\r\n", msg->account->domain); /* TODO */
  378. purple_socket_write(msg->socket, (guchar *) ehlo, strlen(ehlo));
  379. msg->state++;
  380. } else {
  381. goto error;
  382. }
  383. } else if (msg->state == SMTP_HELLO) {
  384. if (buffer[0] == '2' && buffer[1] == '5' && buffer[2] == '0') {
  385. /* Authenticate with plain */
  386. gchar *username = email_address_component(purple_account_get_username(msg->account->account), COMPONENT_USER);
  387. const gchar *password = purple_account_get_password(msg->account->account);
  388. size_t sz = strlen(username) + strlen(password) + 2;
  389. guchar *buffer = g_malloc(sz);
  390. buffer[0] = '\0';
  391. memcpy(buffer + 1, (guchar*) username, strlen(username));
  392. buffer[1 + strlen(username)] = '\0';
  393. memcpy(buffer + 1 + strlen(username) + 1, (guchar *) password, strlen(password));
  394. gchar *base64 = g_base64_encode(buffer, sz);
  395. g_free(buffer);
  396. g_free(username);
  397. gchar *cmd = g_strdup_printf("AUTH PLAIN %s\r\n", base64);
  398. purple_socket_write(msg->socket, (guchar *) cmd, strlen(cmd));
  399. msg->state++;
  400. g_free(base64);
  401. } else {
  402. goto error;
  403. }
  404. } else if (msg->state >= (SMTP_FROM - 1) && msg->state <= (SMTP_DATA - 1)) {
  405. if (buffer[0] == '2') {
  406. /* Send the next piece of information */
  407. msg->state++;
  408. gchar *resp;
  409. switch (msg->state) {
  410. case SMTP_FROM:
  411. resp = g_strdup_printf("MAIL FROM: <%s>\r\n", msg->sender);
  412. break;
  413. case SMTP_RCPT:
  414. resp = g_strdup_printf("RCPT TO: <%s>\r\n", msg->recipient);
  415. break;
  416. case SMTP_DATA:
  417. resp = g_strdup_printf("DATA\r\n");
  418. break;
  419. default:
  420. resp = g_strdup("QUIT");
  421. break;
  422. }
  423. purple_socket_write(msg->socket, (guchar *) resp, strlen(resp));
  424. g_free(resp);
  425. } else {
  426. goto error;
  427. }
  428. } else if (msg->state == SMTP_DATA) {
  429. if (buffer[0] == '3' && buffer[1] == '5' && buffer[2] == '4') {
  430. /* TODO: Escape */
  431. /* TODO: Date */
  432. /* TODO: Randomise */
  433. gchar *boundary = "laescuchdabushdetodoslosescuchdabush";
  434. gchar *plain = purple_markup_strip_html(msg->body);
  435. gchar *html = msg->body;
  436. gchar *data = g_strdup_printf(
  437. "From: %s <%s>\r\n"
  438. "To: %s <%s>\r\n"
  439. "Subject: %s\r\n"
  440. "Content-Type: multipart/alternative; boundary=%s\r\n"
  441. //"Date: Tue, 15 January 2008 16:02:43 -0500"
  442. "\r\n"
  443. "--%s\r\n"
  444. "Content-Type: text/plain\r\n"
  445. "\r\n"
  446. "%s\r\n"
  447. "--%s\r\n"
  448. "Content-Type: text/html\r\n"
  449. "\r\n"
  450. "%s\r\n"
  451. "--%s--\r\n"
  452. ".\r\n",
  453. msg->sender_alias, msg->sender,
  454. msg->recipient_alias, msg->recipient,
  455. msg->subject, boundary,
  456. boundary, plain,
  457. boundary, html,
  458. boundary);
  459. purple_socket_write(msg->socket, (guchar *) data, strlen(data));
  460. msg->state++;
  461. g_free(data);
  462. g_free(plain);
  463. } else {
  464. goto error;
  465. }
  466. } else if (msg->state == SMTP_XFER) {
  467. if (buffer[0] == '2' && buffer[1] == '5' && buffer[2] == '0') {
  468. gchar *quit = "QUIT\r\n";
  469. purple_socket_write(msg->socket, (guchar *) quit, strlen(quit));
  470. msg->state++;
  471. } else {
  472. goto error;
  473. }
  474. } else if (msg->state == SMTP_QUIT) {
  475. g_free(msg->sender);
  476. g_free(msg->sender_alias);
  477. g_free(msg->recipient_alias);
  478. g_free(msg->subject);
  479. g_free(msg->body);
  480. purple_socket_destroy(msg->socket);
  481. g_free(msg);
  482. }
  483. goto not_err;
  484. error:
  485. printf("Error!\n");
  486. not_err:
  487. buffer[sz] = 0;
  488. puts((gchar *) buffer);
  489. printf("(state %d)\n", msg->state);
  490. return;
  491. }
  492. }
  493. void
  494. email_smtp_connected(PurpleSocket *ps, const gchar *error, gpointer user_data)
  495. {
  496. if (error) {
  497. printf("Error SMTP connecting %s\n", error);
  498. return;
  499. }
  500. printf("SMTP Connected\n");
  501. purple_socket_watch(ps, PURPLE_INPUT_READ, email_got_smtp, user_data);
  502. }
  503. void
  504. email_send_smtp(EmailAccount *account, OutgoingMessage *msg)
  505. {
  506. msg->socket = purple_socket_new(account->pc);
  507. purple_socket_set_tls(msg->socket, TRUE);
  508. purple_socket_set_host(msg->socket, purple_account_get_string(account->account, "smtp_server", account->domain));
  509. purple_socket_set_port(msg->socket, SMTPS_PORT);
  510. purple_socket_connect(msg->socket, email_smtp_connected, msg);
  511. }
  512. static int
  513. email_send_im(PurpleConnection *pc,
  514. #if PURPLE_VERSION_CHECK(3, 0, 0)
  515. PurpleMessage *msg)
  516. {
  517. const gchar *who = purple_message_get_recipient(msg);
  518. const gchar *message = purple_message_get_contents(msg);
  519. #else
  520. const gchar *who, const gchar *message, PurpleMessageFlags flags)
  521. {
  522. #endif
  523. EmailAccount *da = purple_connection_get_protocol_data(pc);
  524. OutgoingMessage *outgoing = g_new0(OutgoingMessage, 1);
  525. outgoing->account = da;
  526. outgoing->sender = g_strdup(purple_account_get_username(da->account));
  527. outgoing->recipient = g_strdup(who);
  528. PurpleBuddy *buddy = purple_blist_find_buddy(da->account, who);
  529. if (purple_account_get_private_alias(da->account)) {
  530. outgoing->sender_alias = g_strdup(purple_account_get_private_alias(da->account));
  531. } else {
  532. outgoing->sender_alias = g_strdup(who);
  533. }
  534. if (buddy && purple_buddy_get_alias(buddy)) {
  535. outgoing->recipient_alias = g_strdup(purple_buddy_get_alias(buddy));
  536. } else {
  537. outgoing->recipient_alias = g_strdup(who);
  538. }
  539. outgoing->subject = g_strdup("Pidgin message");
  540. outgoing->body = g_strdup(message);
  541. outgoing->state = SMTP_ANONYMOUS;
  542. email_send_smtp(da, outgoing);
  543. return 1;
  544. }
  545. static GList *
  546. email_add_account_options(GList *account_options)
  547. {
  548. PurpleAccountOption *option;
  549. option = purple_account_option_string_new(_("SMTP server"), "smtp_server", "");
  550. account_options = g_list_append(account_options, option);
  551. option = purple_account_option_string_new(_("IMAP server"), "imap_server", "");
  552. account_options = g_list_append(account_options, option);
  553. return account_options;
  554. }
  555. static const char *
  556. email_list_icon(PurpleAccount *account, PurpleBuddy *buddy)
  557. {
  558. return "email";
  559. }
  560. static void
  561. plugin_init(PurplePlugin *plugin)
  562. {
  563. #ifdef ENABLE_NLS
  564. bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
  565. bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
  566. #endif
  567. PurplePluginInfo *info;
  568. PurplePluginProtocolInfo *prpl_info = g_new0(PurplePluginProtocolInfo, 1);
  569. info = plugin->info;
  570. if (info == NULL) {
  571. plugin->info = info = g_new0(PurplePluginInfo, 1);
  572. }
  573. info->extra_info = prpl_info;
  574. #if PURPLE_MINOR_VERSION >= 5
  575. prpl_info->struct_size = sizeof(PurplePluginProtocolInfo);
  576. #endif
  577. prpl_info->options = OPT_PROTO_CHAT_TOPIC | OPT_PROTO_SLASH_COMMANDS_NATIVE | OPT_PROTO_UNIQUE_CHATNAME;
  578. prpl_info->protocol_options = email_add_account_options(prpl_info->protocol_options);
  579. prpl_info->icon_spec.format = "png,gif,jpeg";
  580. prpl_info->icon_spec.min_width = 0;
  581. prpl_info->icon_spec.min_height = 0;
  582. prpl_info->icon_spec.max_width = 96;
  583. prpl_info->icon_spec.max_height = 96;
  584. prpl_info->icon_spec.max_filesize = 0;
  585. prpl_info->icon_spec.scale_rules = PURPLE_ICON_SCALE_DISPLAY;
  586. prpl_info->get_account_text_table = email_get_account_text_table;
  587. prpl_info->status_types = email_status_types;
  588. prpl_info->list_icon = email_list_icon;
  589. prpl_info->chat_info = email_chat_info;
  590. prpl_info->chat_info_defaults = email_chat_info_defaults;
  591. prpl_info->login = email_login;
  592. prpl_info->close = email_close;
  593. prpl_info->send_im = email_send_im;
  594. // prpl_info->join_chat = email_join_chat;
  595. // prpl_info->get_chat_name = email_get_chat_name;
  596. // prpl_info->chat_invite = email_chat_invite;
  597. // prpl_info->chat_send = email_chat_send;
  598. // prpl_info->set_chat_topic = email_chat_set_topic;
  599. // prpl_info->roomlist_get_list = email_roomlist_get_list;
  600. // prpl_info->roomlist_room_serialize = email_roomlist_serialize;
  601. }
  602. static PurplePluginInfo info = {
  603. PURPLE_PLUGIN_MAGIC,
  604. /* PURPLE_MAJOR_VERSION,
  605. PURPLE_MINOR_VERSION,
  606. */
  607. 2, 1,
  608. PURPLE_PLUGIN_PROTOCOL, /* type */
  609. NULL, /* ui_requirement */
  610. 0, /* flags */
  611. NULL, /* dependencies */
  612. PURPLE_PRIORITY_DEFAULT, /* priority */
  613. EMAIL_PLUGIN_ID, /* id */
  614. "Email", /* name */
  615. EMAIL_PLUGIN_VERSION, /* version */
  616. "", /* summary */
  617. "", /* description */
  618. "Alyssa Rosenzweig <alyssa@rosenzweig.io>", /* author */
  619. EMAIL_PLUGIN_WEBSITE, /* homepage */
  620. libpurple2_plugin_load, /* load */
  621. libpurple2_plugin_unload, /* unload */
  622. NULL, /* destroy */
  623. NULL, /* ui_info */
  624. NULL, /* extra_info */
  625. NULL, /* prefs_info */
  626. /*email_actions*/ NULL, /* actions */
  627. NULL, /* padding */
  628. NULL,
  629. NULL,
  630. NULL
  631. };
  632. PURPLE_INIT_PLUGIN(email, plugin_init, info);
  633. #else
  634. /* Purple 3 plugin load functions */
  635. G_MODULE_EXPORT GType email_protocol_get_type(void);
  636. #define EMAIL_TYPE_PROTOCOL (email_protocol_get_type())
  637. #define EMAIL_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), EMAIL_TYPE_PROTOCOL, EmailProtocol))
  638. #define EMAIL_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EMAIL_TYPE_PROTOCOL, EmailProtocolClass))
  639. #define EMAIL_IS_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), EMAIL_TYPE_PROTOCOL))
  640. #define EMAIL_IS_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), EMAIL_TYPE_PROTOCOL))
  641. #define EMAIL_PROTOCOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), EMAIL_TYPE_PROTOCOL, EmailProtocolClass))
  642. typedef struct _EmailProtocol {
  643. PurpleProtocol parent;
  644. } EmailProtocol;
  645. typedef struct _EmailProtocolClass {
  646. PurpleProtocolClass parent_class;
  647. } EmailProtocolClass;
  648. static void
  649. email_protocol_init(PurpleProtocol *prpl_info)
  650. {
  651. PurpleProtocol *info = prpl_info;
  652. info->id = EMAIL_PLUGIN_ID;
  653. info->name = "Email";
  654. info->options = OPT_PROTO_CHAT_TOPIC | OPT_PROTO_SLASH_COMMANDS_NATIVE | OPT_PROTO_UNIQUE_CHATNAME;
  655. info->account_options = email_add_account_options(info->account_options);
  656. }
  657. static void
  658. email_protocol_class_init(PurpleProtocolClass *prpl_info)
  659. {
  660. prpl_info->login = email_login;
  661. prpl_info->close = email_close;
  662. prpl_info->list_icon = email_list_icon;
  663. prpl_info->status_type = email_status_types;
  664. }
  665. static void
  666. email_protocol_im_iface_init(PurpleProtocolIMIface *prpl_info)
  667. {
  668. prpl_info->send = email_send_im;
  669. }
  670. static void
  671. email_protocol_chat_iface_init(PurpleProtocolChatIface *prpl_info)
  672. {
  673. prpl_info->send = email_chat_send;
  674. prpl_info->info = email_chat_info;
  675. prpl_info->info_defaults = email_chat_info_defaults;
  676. // prpl_info->join = email_join_chat;
  677. // prpl_info->get_name = email_get_chat_name;
  678. // prpl_info->invite = email_chat_invite;
  679. // prpl_info->set_topic = email_chat_set_topic;
  680. }
  681. static void
  682. email_protocol_server_iface_init(PurpleProtocolServerIface *prpl_info)
  683. {
  684. }
  685. static void
  686. email_protocol_client_iface_init(PurpleProtocolClientIface *prpl_info)
  687. {
  688. prpl_info->get_account_text_table = email_get_account_text_table;
  689. // prpl_info->get_actions = email_actions;
  690. // prpl_info->tooltip_text = email_tooltip_text;
  691. }
  692. static void
  693. email_protocol_privacy_iface_init(PurpleProtocolPrivacyIface *prpl_info)
  694. {
  695. // prpl_info->add_deny = email_block_user;
  696. // prpl_info->rem_deny = email_unblock_user;
  697. }
  698. static void
  699. email_protocol_roomlist_iface_init(PurpleProtocolRoomlistIface *prpl_info)
  700. {
  701. // prpl_info->get_list = email_roomlist_get_list;
  702. // prpl_info->room_serialize = email_roomlist_serialize;
  703. }
  704. static PurpleProtocol *email_protocol;
  705. PURPLE_DEFINE_TYPE_EXTENDED(
  706. EmailProtocol, email_protocol, PURPLE_TYPE_PROTOCOL, 0,
  707. PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_IM_IFACE,
  708. email_protocol_im_iface_init)
  709. PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CHAT_IFACE,
  710. email_protocol_chat_iface_init)
  711. PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_SERVER_IFACE,
  712. email_protocol_server_iface_init)
  713. PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CLIENT_IFACE,
  714. email_protocol_client_iface_init)
  715. PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_PRIVACY_IFACE,
  716. email_protocol_privacy_iface_init)
  717. PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_ROOMLIST_IFACE,
  718. email_protocol_roomlist_iface_init)
  719. );
  720. static gboolean
  721. libpurple3_plugin_load(PurplePlugin *plugin, GError **error)
  722. {
  723. email_protocol_register_type(plugin);
  724. email_protocol = purple_protocols_add(EMAIL_TYPE_PROTOCOL, error);
  725. if (!email_protocol) {
  726. return FALSE;
  727. }
  728. return plugin_load(plugin, error);
  729. }
  730. static gboolean
  731. libpurple3_plugin_unload(PurplePlugin *plugin, GError **error)
  732. {
  733. if (!plugin_unload(plugin, error)) {
  734. return FALSE;
  735. }
  736. if (!purple_protocols_remove(email_protocol, error)) {
  737. return FALSE;
  738. }
  739. return TRUE;
  740. }
  741. static PurplePluginInfo *
  742. plugin_query(GError **error)
  743. {
  744. #ifdef ENABLE_NLS
  745. bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
  746. bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
  747. #endif
  748. return purple_plugin_info_new(
  749. "id", EMAIL_PLUGIN_ID,
  750. "name", "Email",
  751. "version", EMAIL_PLUGIN_VERSION,
  752. "category", _("Protocol"),
  753. "summary", _("Email Protocol Plugins."),
  754. "description", _("Adds Email protocol support to libpurple."),
  755. "website", EMAIL_PLUGIN_WEBSITE,
  756. "abi-version", PURPLE_ABI_VERSION,
  757. "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL |
  758. PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD,
  759. NULL);
  760. }
  761. PURPLE_PLUGIN_INIT(email, plugin_query, libpurple3_plugin_load, libpurple3_plugin_unload);
  762. #endif