pax_global_header00006660000000000000000000000064137114555120014516gustar00rootroot0000000000000052 comment=a48071a9787167fdbf10673cb6e076018e74f041 purple-mm-sms-0.1.7/000077500000000000000000000000001371145551200142415ustar00rootroot00000000000000purple-mm-sms-0.1.7/.gitignore000066400000000000000000000001541371145551200162310ustar00rootroot00000000000000*.o *.so *.log *~ build debian/.debhelper debian/files debian/purple-mm-sms.substvars debian/purple-mm-sms/ purple-mm-sms-0.1.7/.gitlab-ci.yml000066400000000000000000000013771371145551200167050ustar00rootroot00000000000000include: - 'https://source.puri.sm/Librem5/librem5-ci/raw/master/librem5-pipeline-definitions.yml' stages: - build - package before_script: - export DEBIAN_FRONTEND=noninteractive - apt-get -y update - apt-get -y install build-essential wget ca-certificates gnupg - echo "deb http://ci.puri.sm/ scratch librem5" > /etc/apt/sources.list.d/ci.list - wget -O- https://ci.puri.sm/ci-repo.key | apt-key add - - apt-get -y update - apt-get -y build-dep . build-debian-gcc-buster: image: debian:buster stage: build tags: - librem5 script: - export LC_ALL=C.UTF-8 - make package:deb-debian-buster: extends: .l5-build-debian-package package:deb-debian-buster:arm64: tags: - librem5:arm64 extends: .l5-build-debian-package purple-mm-sms-0.1.7/Makefile000066400000000000000000000021101371145551200156730ustar00rootroot00000000000000 CC = gcc LIBS = purple mm-glib PKG_CONFIG = pkg-config CFLAGS += $(shell $(PKG_CONFIG) --cflags $(LIBS)) CFLAGS += -fPIC -DPIC LDLIBS += $(shell $(PKG_CONFIG) --libs $(LIBS)) DIR_PERM = 0755 FILE_PERM = 0644 PLUGIN_DIR_PURPLE = $(shell $(PKG_CONFIG) --variable=plugindir purple) DATA_ROOT_DIR_PURPLE = $(shell $(PKG_CONFIG) --variable=datarootdir purple) TARGET = mm-sms.so COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c CFLAGS += -Wall -g -O0 #-Werror CFLAGS += -DPURPLE_PLUGINS OBJECTS = mm-sms.o all: $(TARGET) clean: rm -f $(OBJECTS) $(OBJECTS:.o=.d) $(TARGET) install: mkdir -p $(DESTDIR)$(PLUGIN_DIR_PURPLE) install -m $(FILE_PERM) $(TARGET) $(DESTDIR)$(PLUGIN_DIR_PURPLE) for i in 16 22 48; do \ mkdir -p $(DESTDIR)$(DATA_ROOT_DIR_PURPLE)/pixmaps/pidgin/protocols/$$i; \ install -m $(FILE_PERM) icons/mm-sms-$${i}px.png $(DESTDIR)$(DATA_ROOT_DIR_PURPLE)/pixmaps/pidgin/protocols/$$i/mm-sms.png; \ done $(TARGET): $(OBJECTS) $(LINK.o) -shared $^ $(LOADLIBES) $(LDLIBS) -o $@ %.o: %.c $(COMPILE.c) $(OUTPUT_OPTION) $< -include $(OBJECTS:.o) purple-mm-sms-0.1.7/README.md000066400000000000000000000016121371145551200155200ustar00rootroot00000000000000# purple-mm-sms A libpurple plugin for sending and receiving SMS via Modemmanager ## Build and install ### Install dependencies ``` bash sudo apt install libpurple-dev libmm-glib-dev modemmanager ``` ### Build and install purple-mm-sms ``` bash make make install ``` ## How to use it Launch the "Manage Accounts" dialog in Pidgin. Choose 'ModemManager SMS' from the protocols list. Enter any string in the username entry and the modem-pin in the password-entry. The pin is optional, no need to enter it if the modem is already unlocked. ## Purple commands - '/mm-sms help': Displays a list with available commands - '/mm-sms info': Show modem info - '/mm-sms rm_s [on; off]': Remove SMS from modem when sent - '/mm-sms rm_r [on; off]': Remove SMS from modem when received - '/mm-sms deli [on; off]': Request delivery reports from SMC - '/mm-sms vali [2; 7; 30]': Set SMS validity period in days purple-mm-sms-0.1.7/debian/000077500000000000000000000000001371145551200154635ustar00rootroot00000000000000purple-mm-sms-0.1.7/debian/changelog000066400000000000000000000060731371145551200173430ustar00rootroot00000000000000purple-mm-sms (0.1.7) amber-phone; urgency=medium [ Salem Elrahal ] * simple handling of CDMA deliver PDU type -- Mohammed Sadiq Sun, 02 Aug 2020 11:49:37 +0530 purple-mm-sms (0.1.6) amber-phone; urgency=high * Strip invalid characters from phone number -- Mohammed Sadiq Wed, 15 Jul 2020 19:29:56 +0530 purple-mm-sms (0.1.5) amber-phone; urgency=medium * ci: Use standard definitions * Free device the right way * Add API to get SIM country code * Watch for modem manager changes * Don't parse NULL strings * debian: Add gbp.conf -- Mohammed Sadiq Tue, 14 Jul 2020 14:35:57 +0530 purple-mm-sms (0.1.4) amber; urgency=medium [ Mohammed Sadiq ] * Load only supported modems on startup -- Andrea Schaefer Thu, 12 Mar 2020 14:09:17 +0100 purple-mm-sms (0.1.3) amber-phone; urgency=medium [ Mohammed Sadiq ] * Load modems only when Messaging is available -- Andrea Schaefer Wed, 26 Feb 2020 11:58:40 +0100 purple-mm-sms (0.1.2) amber-phone; urgency=medium [ Mohammed Sadiq ] * Warn and return early if messaging is NULL [ Sebastian Krzyszkowiak ] * Report connection failure when the modem is disabled [ Andrea Schaefer ] * Replace g_error with g_debug -- Andrea Schaefer Wed, 22 Jan 2020 12:55:32 +0100 purple-mm-sms (0.1.1) amber-phone; urgency=high * Add instance checks * Add amendments to instance check -- Andrea Schaefer Sun, 15 Dec 2019 12:18:01 +0100 purple-mm-sms (0.1.0) amber-phone; urgency=medium [ Mohammed Sadiq ] * Fix a possible NULL dereference -- Andrea Schaefer Tue, 19 Nov 2019 13:15:58 +0100 purple-mm-sms (0.0.5) amber-phone; urgency=medium * Changed handles for purple signals -- Andrea Schaefer Sat, 05 Oct 2019 09:36:29 +0200 purple-mm-sms (0.0.4) purple; urgency=medium * Add support for 'offline' messages * Add support for multipart SMS * Add support for delivery reports * Fix issue in "modem_added" callback handler * Improve plugin startup and modem reconnection * Add SIM card support for entering PIN code when plugin is used with Pidgin -- Andrea Schaefer Thu, 15 Aug 2019 15:52:27 +0200 purple-mm-sms (0.0.3) purple; urgency=medium * Reworked code by using libmm-glib instead of g_dbus_proxy calls -- Andrea Schaefer Thu, 16 May 2019 13:52:43 +0200 purple-mm-sms (0.0.2) purple; urgency=medium * Fixed a bug that led to a segfault when accessing the messaging proxy -- Andrea Schaefer Fri, 05 Apr 2019 10:11:40 +0200 purple-mm-sms (0.0.1) purple; urgency=medium * Added purple-signal for delivery status * Added purple-commands -- Andrea Schaefer Fri, 14 Dec 2018 13:19:47 +0100 purple-mm-sms (0.0.0) unstable; urgency=medium * Initial release. -- Guido Günther Sat, 20 Oct 2018 18:52:30 +0200 purple-mm-sms-0.1.7/debian/compat000066400000000000000000000000031371145551200166620ustar00rootroot0000000000000011 purple-mm-sms-0.1.7/debian/control000066400000000000000000000007721371145551200170740ustar00rootroot00000000000000Source: purple-mm-sms Section: net Priority: optional Maintainer: Andrea Schaefer Build-Depends: debhelper (>= 11), libpurple-dev, modemmanager-dev, libmm-glib-dev, Standards-Version: 4.2.1 Homepage: https://source.puri.sm/Librem5/purple-mm-sms Package: purple-mm-sms Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, modemmanager, Description: libpurple plugin for SMS Plugin for libpurple which adds the ability to communicate via SMS using ModemManager. purple-mm-sms-0.1.7/debian/copyright000066400000000000000000000017151371145551200174220ustar00rootroot00000000000000Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: purple-mm-sms Source: https://source.puri.sm/Librem5/purple-mm-sms Files: * Copyright: 2018 Purism SPC License: GPL-3+ 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; either version 3 of the License, or (at your option) any later version. . 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 program. If not, see . On Debian systems, the complete text of the GNU General Public License version 3 can be found in "/usr/share/common-licenses/GPL-3". purple-mm-sms-0.1.7/debian/docs000066400000000000000000000000121371145551200163270ustar00rootroot00000000000000README.md purple-mm-sms-0.1.7/debian/gbp.conf000066400000000000000000000002061371145551200171000ustar00rootroot00000000000000[DEFAULT] debian-branch = master debian-tag = v%(version)s debian-tag-msg = purple-mm-sms v%(version)s [dch] multimaint-merge = True purple-mm-sms-0.1.7/debian/rules000077500000000000000000000001171371145551200165420ustar00rootroot00000000000000#!/usr/bin/make -f export DEB_BUILD_MAINT_OPTIONS = hardening=+all %: dh $@ purple-mm-sms-0.1.7/debian/source/000077500000000000000000000000001371145551200167635ustar00rootroot00000000000000purple-mm-sms-0.1.7/debian/source/format000066400000000000000000000000151371145551200201720ustar00rootroot000000000000003.0 (native) purple-mm-sms-0.1.7/icons/000077500000000000000000000000001371145551200153545ustar00rootroot00000000000000purple-mm-sms-0.1.7/icons/mm-sms-16px.png000066400000000000000000000040021371145551200200630ustar00rootroot00000000000000PNG  IHDRaiTXtXML:com.adobe.xmp %iCCPsRGB IEC61966-2.1(u+DQ?3hİB~Ԅ24Q7ϼ53^$[e(k_VY+EdaeMls759s~={w,fF4NgOTӀ!G"0%Zk5ISWy1r^yKIM+'\P?ۜrf# ^؛ʼn__6c6*ނI3J? 짋>eE %WYgR)TOJDOȰbo_Mϩ^GzmMڰ:8aM^6CʏT&4x984^Aӳ>Gw[]o.g t pHYs  nIDAT8?HWa3-b[$ E-ACCCMҟ!pihUPZr O=^4:p9pԮ&|r~ v2(IpC8cߘBlk>V44q1[dt0o;i.c3M ts;y_[!wcyp+n{;`ip;vIENDB`purple-mm-sms-0.1.7/icons/mm-sms-22px.png000066400000000000000000000043001371145551200200610ustar00rootroot00000000000000PNG  IHDRĴl;iTXtXML:com.adobe.xmp eYfiCCPsRGB IEC61966-2.1(u+DQ?3hİB~Ԅ24Q7ϼ53^$[e(k_VY+EdaeMls759s~={w,fF4NgOTӀ!G"0%Zk5ISWy1r^yKIM+'\P?ۜrf# ^؛ʼn__6c6*ނI3J? 짋>eE %WYgR)TOJDOȰbo_Mϩ^GzmMڰ:8aM^6CʏT&4x984^Aӳ>Gw[]o.g t pHYs  ,IDAT8KQ3[DcB6^HY(dFFFb%d!)/qOƒ 0y{xGU1І%ԅ+ 6_oq2+jLE7N'np/XQ\a|alІh)߬s+OX^V|]Q\nmƺ#f\2it\,{6jX8t  `MԗphZy_pxS%7O.K 38XƼcxR]/sf,7 `Jp|kms5B0\VσaP+[3yYQn;X^¥Sx`:# #iCCPsRGB IEC61966-2.1(u+DQ?3hİB~Ԅ24Q7ϼ53^$[e(k_VY+EdaeMls759s~={w,fF4NgOTӀ!G"0%Zk5ISWy1r^yKIM+'\P?ۜrf# ^؛ʼn__6c6*ނI3J? 짋>eE %WYgR)TOJDOȰbo_Mϩ^GzmMڰ:8aM^6CʏT&4x984^Aӳ>Gw[]o.g t pHYs  IDAThk]EJPch@1QF14Q&F`@mD4iH14Vhm}D-E1tsៜgZ33kZ&iy ^#m'DJ|;{+2L݋;qWK=ZLF,QϽ w&[U!M~d7x2;rV%5h? [Uxa>C؁[Yb/+ž#0~mGf"FCxyG":ff&FCDM!b\9o3^ۇ)𢱤&<g4X݋I!0Ah-HCl1c<&ȼ녉n5:, UbSxd=E'qKdD`,#qô:۩QNE}؇6ϛw^ӽ%m}mo'+{s }^9Z]mq\܊+Rd|7/.W>gCxՉBMX7zߏBc˱Nd lJl5jAbeG\5晦FO <5_O\=Lq߷z2,ćt뚯{+dT<ʒ2 >?~nΞTh4sSC)@m-}E d'ϊ*n[0rk\rʻu iOX/&]8/8b2e?vL eN2O=L11{+(ӊKCfX#-ʎkl**Hwj0. ]S<wsb]c2_3hs9ы|3dN\9++yX@kF5g_4 * * SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once #include struct mcc_list { guint mcc; char code[3]; }; /* * Extracted from: * https://www.itu.int/dms_pub/itu-t/opb/sp/T-SP-E.212B-2018-PDF-E.pdf */ struct mcc_list mcc_list[] = { {202, "GR"}, {204, "NL"}, {206, "BE"}, {208, "FR"}, {212, "MC"}, {213, "AD"}, {214, "ES"}, {216, "HU"}, {218, "BA"}, {219, "HR"}, {220, "RS"}, {221, "XK"}, {222, "IT"}, {225, "VA"}, {226, "RO"}, {228, "CH"}, {230, "CZ"}, {231, "SK"}, {232, "AT"}, {234, "GB"}, {235, "GB"}, {238, "DK"}, {240, "SE"}, {242, "NO"}, {244, "FI"}, {246, "LT"}, {247, "LV"}, {248, "EE"}, {250, "RU"}, {255, "UA"}, {257, "BY"}, {259, "MD"}, {260, "PL"}, {262, "DE"}, {266, "GI"}, {268, "PT"}, {270, "LU"}, {272, "IE"}, {274, "IS"}, {276, "AL"}, {278, "MT"}, {280, "CY"}, {282, "GE"}, {283, "AM"}, {284, "BG"}, {286, "TR"}, {288, "FO"}, {290, "GL"}, {292, "SM"}, {293, "SI"}, {294, "MK"}, {295, "LI"}, {297, "ME"}, {302, "CA"}, {308, "PM"}, {310, "US"}, {311, "US"}, {312, "US"}, {313, "US"}, {314, "US"}, {315, "US"}, {316, "US"}, {330, "PR"}, {332, "VI"}, {334, "MX"}, {338, "JM"}, /* Guadeloupe and Martinique are part of France */ {340, "GP"}, {340, "MQ"}, {342, "BB"}, {344, "AG"}, {346, "KY"}, {348, "VG"}, {350, "BM"}, {352, "GD"}, {354, "MS"}, {356, "KN"}, {358, "LC"}, {360, "VC"}, {362, "CW"}, {363, "AW"}, {364, "BS"}, {365, "AI"}, {366, "DM"}, {368, "CU"}, {370, "DO"}, {372, "HT"}, {374, "TT"}, {376, "TC"}, {400, "AZ"}, {401, "KZ"}, {402, "BT"}, {404, "IN"}, {405, "IN"}, {406, "IN"}, {410, "PK"}, {412, "AF"}, {413, "LK"}, {414, "MM"}, {415, "LB"}, {416, "JO"}, {417, "SY"}, {418, "IQ"}, {419, "KW"}, {420, "SA"}, {421, "YE"}, {422, "OM"}, {424, "AE"}, {425, "IL"}, {426, "BH"}, {427, "QA"}, {428, "MN"}, {429, "NP"}, {430, "AE"}, {431, "AE"}, {432, "IR"}, {434, "UZ"}, {436, "TJ"}, {437, "KG"}, {438, "TM"}, {440, "JP"}, {441, "JP"}, {450, "KP"}, {452, "VN"}, {454, "HK"}, {455, "MO"}, {456, "KH"}, {457, "LA"}, {460, "CN"}, {461, "CN"}, {466, "TW"}, {467, "KR"}, {470, "BD"}, {472, "MV"}, {502, "MY"}, {505, "AU"}, {510, "ID"}, {514, "TL"}, {515, "PH"}, {520, "TH"}, {525, "SG"}, {528, "BN"}, {530, "NZ"}, {536, "NR"}, {537, "PG"}, {539, "TO"}, {540, "SB"}, {541, "VU"}, {542, "FJ"}, {543, "WF"}, {544, "AS"}, {545, "KI"}, {546, "NC"}, {547, "PF"}, {548, "CK"}, {549, "AS"}, {550, "FM"}, {551, "MH"}, {552, "PW"}, {553, "TV"}, {554, "TK"}, {555, "NU"}, {602, "EG"}, {603, "DZ"}, {604, "MA"}, {605, "TN"}, {606, "LY"}, {607, "GM"}, {608, "SN"}, {609, "MR"}, {610, "ML"}, {611, "GN"}, {612, "CI"}, {613, "BF"}, {614, "NE"}, {615, "TG"}, {616, "BJ"}, {617, "MU"}, {618, "LR"}, {619, "SL"}, {620, "GH"}, {621, "NG"}, {622, "TD"}, {623, "CF"}, {624, "CM"}, {625, "CV"}, {626, "ST"}, {627, "GQ"}, {628, "GA"}, {629, "CG"}, {630, "CD"}, {631, "AO"}, {632, "GW"}, {633, "SC"}, {634, "SD"}, {635, "RW"}, {636, "ET"}, {637, "SO"}, {638, "DJ"}, {639, "KE"}, {640, "TZ"}, {641, "UG"}, {642, "BI"}, {643, "MZ"}, {645, "ZM"}, {646, "MG"}, {647, "RE"}, {648, "ZW"}, {649, "NA"}, {650, "MW"}, {651, "LS"}, {652, "BW"}, {653, "SZ"}, {654, "KM"}, {655, "ZA"}, {657, "ER"}, {658, "SH"}, {659, "SS"}, {702, "BZ"}, {704, "GT"}, {706, "SV"}, {708, "HN"}, {710, "NI"}, {712, "CR"}, {714, "PA"}, {716, "PE"}, {722, "AR"}, {724, "BR"}, {730, "CL"}, {732, "CO"}, {734, "VE"}, {736, "BO"}, {738, "GY"}, {740, "EC"}, {742, "GF"}, {744, "PY"}, {746, "SR"}, {748, "UY"}, {750, "FK"}, }; /* * @mcc_str should have MCC as prefix, * It doesn't matter if any thing else is followed. * So it's okay to pass an IMSI. */ static inline const char * get_country_iso_for_mcc (const char *mcc_str) { g_autofree char *str = NULL; guint64 mcc; if (!mcc_str || strlen (mcc_str) < 3) return NULL; str = g_strndup (mcc_str, 3); mcc = g_ascii_strtoull (str, NULL, 10); for (guint i = 0; i < G_N_ELEMENTS (mcc_list); i++) if (mcc_list[i].mcc == mcc) return mcc_list[i].code; g_warning ("invalid MCC code: %" G_GUINT64_FORMAT, mcc); return NULL; } purple-mm-sms-0.1.7/mm-sms.c000066400000000000000000001310771371145551200156270ustar00rootroot00000000000000/* * Copyright (C) 2018 Purism SPC * * 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, see . * */ #define G_LOG_DOMAIN "mm-sms" #define _GNU_SOURCE 1 #include #include #include #include #include #include #include #include #include "mm-sms.h" #include "itu-e212-iso.h" #define PUR_MM_SMS_PLUGIN_VERSION "0.1.7" #define PUR_MM_MODEM_TIMEOUT 20000 #define PUR_MM_MODEM_SMS_TIMEOUT 35000 // Relative Validity Period Values according to GSM spec // 0–143 | (TP-VP + 1) x 5 minutes // 144–167 | (12 + (TP-VP - 143) / 2 ) hours // 168–196 | (TP-VP - 166) days // 197–255 | (TP-VP - 192) weeks #define SMS_VALIDITY_2_DAYS 168 #define SMS_VALIDITY_7_DAYS 173 #define SMS_VALIDITY_30_DAYS 196 #define SMS_VALIDITY_NOT_SET 0 static pur_mm_data_t pur_mm_data; pur_mm_data_t *pur_mm_get_data (void) { return &pur_mm_data; } typedef struct { MMObject *object; MMModem *modem; MMSim *sim; } PurMmDevice; typedef struct { MMSms *sms; MMSmsState state; const gchar *number; const gchar *message; guint message_ref; time_t t_stamp; const gchar *path; gchar *sms_id; } PurSmsProps; static void pur_mm_state (int state); static void pur_mm_get_modems (void); static void pur_mm_purple_connect (void); static void pur_mm_signal_emit (gchar *sms_id, int state); static void pur_mm_get_new_sms (char *path); static void pur_mm_delete_sms (PurSmsProps *sms_props); static void pur_mm_check_pdu_type (PurSmsProps *sms_props); static void pur_mm_get_sms_properties (PurSmsProps *sms_props); static void pur_mm_send_sms_to_purple_serv (PurSmsProps *sms_props); PurplePlugin *mm_sms_plugin = NULL; static PurpleCmdRet cb_mm_sms_cmd (PurpleConversation *conv, const gchar *cmd, gchar **args, gchar **error, void *data) { char *msg = NULL; pur_mm_data_t *mm_sms = pur_mm_get_data(); if (args[0] == NULL || !g_strcmp0 (args[0], "help")) { msg = g_strdup ("Available commands:\n\n" " - '/mm-sms help': Displays this message\n" " - '/mm-sms info': Show modem info\n" " - '/mm-sms rm_s [on; off]': Remove SMS from modem when sent\n" " - '/mm-sms rm_r [on; off]': Remove SMS from modem when received\n" " - '/mm-sms deli [on; off]': Request delivery reports from SMC\n" " - '/mm-sms vali [2; 7; 30]': Set SMS validity period in days\n"); } else if (!g_strcmp0 (args[0], "info")) { msg = g_strdup_printf ("Manufacturer: %s\n" "Model: %s\n" "Revision: %s\n" "Hardware Version: %s\n" "Device Id: %s\n", mm_modem_dup_manufacturer (mm_sms->modem), mm_modem_dup_model (mm_sms->modem), mm_modem_dup_revision (mm_sms->modem), mm_modem_dup_hardware_revision (mm_sms->modem), mm_modem_dup_device_identifier (mm_sms->modem)); } else if (!g_strcmp0 (args[1], "on")) { if (!g_strcmp0 (args[0], "rm_s")) { purple_account_set_bool (mm_sms->account, "sms_delete_sent", TRUE); mm_sms->sms_delete_sent = TRUE; msg = g_strdup ("SMS will be removed from modem storage"); } else if (!g_strcmp0 (args[0], "rm_r")) { purple_account_set_bool (mm_sms->account, "sms_delete_received", TRUE); mm_sms->sms_delete_received = TRUE; msg = g_strdup ("SMS will be removed from modem storage"); } else if (!g_strcmp0 (args[0], "deli")) { purple_account_set_bool (mm_sms->account, "sms_delivery_report", TRUE); mm_sms->sms_delivery_report = TRUE; msg = g_strdup ("Delivery reports will be requested\n" "(Your SMC may not support delivery reports)"); } } else if (!g_strcmp0 (args[1], "off")) { if (!g_strcmp0 (args[0], "rm_s")) { purple_account_set_bool (mm_sms->account, "sms_delete_sent", FALSE); mm_sms->sms_delete_sent = FALSE; msg = g_strdup ("SMS remain in modem storage"); } else if (!g_strcmp0 (args[0], "rm_r")) { purple_account_set_bool (mm_sms->account, "sms_delete_received", FALSE); mm_sms->sms_delete_received = FALSE; msg = g_strdup ("SMS remain in modem storage"); } else if (!g_strcmp0 (args[0], "deli")) { purple_account_set_bool (mm_sms->account, "sms_delivery_report", FALSE); mm_sms->sms_delivery_report = FALSE; msg = g_strdup ("Delivery reports won't be requested"); } } else if (!g_strcmp0 (args[1], "2")) { if (!g_strcmp0 (args[0], "vali")) { purple_account_set_int (mm_sms->account, "sms_validity", SMS_VALIDITY_2_DAYS); mm_sms->sms_validity = SMS_VALIDITY_2_DAYS; msg = g_strdup ("Set SMS validity period to 2 days\n" "(Your SMC may not supports this setting"); } } else if (!g_strcmp0 (args[1], "7")) { if (!g_strcmp0 (args[0], "vali")) { purple_account_set_int (mm_sms->account, "sms_validity", SMS_VALIDITY_7_DAYS); mm_sms->sms_validity = SMS_VALIDITY_7_DAYS; msg = g_strdup ("Set SMS validity period to 7 days\n" "(Your SMC may not supports this setting"); } } else if (!g_strcmp0 (args[1], "30")) { if (!g_strcmp0 (args[0], "vali")) { purple_account_set_int (mm_sms->account, "sms_validity", SMS_VALIDITY_30_DAYS); mm_sms->sms_validity = SMS_VALIDITY_30_DAYS; msg = g_strdup ("Set SMS validity period to 30 days\n" "(Your SMC may not supports this setting"); } } if (msg) { purple_conversation_write (conv, "mm-sms", msg, PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LOG, time(NULL)); g_free (msg); } return PURPLE_CMD_RET_OK; } static void free_device (PurMmDevice *device) { if (!device) return; g_clear_object (&device->sim); g_clear_object (&device->modem); g_clear_object (&device->object); g_free (device); } static gboolean cb_dbus_signal_sms_added (MMModemMessaging *device, gchar *path, gpointer user_data) { pur_mm_get_new_sms (g_strdup (path)); purple_debug_info ("mm-sms", "New SMS at '%s'", path); g_debug ("%s: New SMS at %s", __func__, path); return TRUE; } static void cb_dbus_signal_sms_deleted (MMModemMessaging *device, gchar *path, gpointer user_data) { g_debug ("%s: Removed SMS at '%s'", __func__, path); } static void cb_sms_delete_finish (MMModemMessaging *modem, GAsyncResult *result, PurSmsProps *sms_props) { g_autoptr(GError) error = NULL; pur_mm_data_t *mm_sms = pur_mm_get_data (); if (mm_modem_messaging_delete_finish (modem, result, &error)) { g_ptr_array_remove (mm_sms->sms_arr, sms_props); g_debug ("Message delete finish"); } else { g_debug ("Couldn't delete SMS - error: %s", error ? error->message : "unknown"); } } static void cb_sms_send_finish (MMSms *sms, GAsyncResult *result, gpointer user_data) { g_autoptr(GError) error = NULL; gboolean fin; pur_mm_data_t *mm_sms = pur_mm_get_data (); fin = mm_sms_send_finish (sms, result, &error); if (!fin) { pur_mm_signal_emit (mm_sms->sms_id, SMS_RECEIPT_NONE); g_debug ("Couldn't send SMS - error: %s", error ? error->message : "unknown"); } else { pur_mm_signal_emit (mm_sms->sms_id, SMS_RECEIPT_MM_ACKN); g_debug ("Successfully sent SMS: %s", mm_sms_get_path (sms)); } } static void cb_sms_create_finish (MMModemMessaging *modem, GAsyncResult *result, gpointer user_data) { MMSms *sms; g_autoptr(GError) error = NULL; sms = mm_modem_messaging_create_finish (modem, result, &error); if (!sms) { g_debug ("Couldn't create new SMS - error: %s", error ? error->message : "unknown"); } else { g_debug ("Successfully created new SMS: %s", mm_sms_get_path (sms)); mm_sms_send (sms, NULL, (GAsyncReadyCallback)cb_sms_send_finish, NULL); g_object_unref (sms); } } static void cb_sms_state_change (MMSms *sms, GParamSpec *pspec, PurSmsProps *sms_props) { pur_mm_get_sms_properties (sms_props); if (sms_props->state == MM_SMS_STATE_RECEIVED) { // The message has been completely received, // hand the SMS message to the libpurple core pur_mm_get_sms_properties (sms_props); pur_mm_send_sms_to_purple_serv (sms_props); } g_debug ("%s: state %s", __func__, mm_sms_state_get_string (mm_sms_get_state (sms))); } static void cb_sms_list_new_ready (MMModemMessaging *modem, GAsyncResult *result, gchar *path) { GList *l, *list; g_autoptr(GError) error = NULL; PurSmsProps *sms_props; pur_mm_data_t *mm_sms = pur_mm_get_data (); list = mm_modem_messaging_list_finish (modem, result, &error); if (!list) { g_debug ("Couldn't get SMS list - error: %s", error ? error->message : "unknown"); } else { for (l = list; l; l = g_list_next (l)) { if (!g_strcmp0 (mm_sms_get_path (MM_SMS (l->data)), path)) { sms_props = g_new0 (PurSmsProps, 1); sms_props->sms = g_object_ref (MM_SMS (l->data)); g_ptr_array_add (mm_sms->sms_arr, sms_props); break; } } pur_mm_check_pdu_type (sms_props); g_list_free_full (list, g_object_unref); g_free (path); } g_debug ("%s", __func__); } static void cb_sms_list_all_ready (MMModemMessaging *modem, GAsyncResult *result, gpointer user_data) { GList *l, *list; g_autoptr(GError) error = NULL; PurSmsProps *sms_props; pur_mm_data_t *mm_sms = pur_mm_get_data (); list = mm_modem_messaging_list_finish (modem, result, &error); if (!list) { g_debug ("Couldn't get SMS list - error: %s", error ? error->message : "unknown"); } else { for (l = list; l; l = g_list_next (l)) { sms_props = g_new0 (PurSmsProps, 1); sms_props->sms = g_object_ref (MM_SMS (l->data)); g_ptr_array_add (mm_sms->sms_arr, sms_props); } g_ptr_array_foreach (mm_sms->sms_arr, (GFunc) pur_mm_check_pdu_type, NULL); g_list_free_full (list, g_object_unref); } g_debug ("%s", __func__); } static void cb_get_sim_ready (MMModem *modem, GAsyncResult *res, gpointer user_data) { const char *code; pur_mm_data_t *mm_sms = pur_mm_get_data (); mm_sms->sim = mm_modem_get_sim_finish (modem, res, NULL); g_debug ("Need to unlock sim %s (%s)", mm_sim_get_path (mm_sms->sim), mm_sim_get_identifier (mm_sms->sim)); code = get_country_iso_for_mcc (mm_sim_get_imsi (mm_sms->sim)); if (code) purple_signal_emit (purple_plugins_get_handle (), "mm-sms-country-code", code); } static gboolean pur_mm_get_modem_state (void) { MMModemState state; pur_mm_data_t *mm_sms = pur_mm_get_data (); if (!mm_sms->modem) { pur_mm_state (PUR_MM_STATE_NO_MODEM); return FALSE; } if (!mm_sms->modem_messaging) { pur_mm_state (PUR_MM_STATE_NO_MESSAGING_MODEM); return FALSE; } state = mm_modem_get_state (mm_sms->modem); if (state < MM_MODEM_STATE_ENABLED) { pur_mm_state (PUR_MM_STATE_MODEM_DISABLED); return FALSE; } return TRUE; } static void pur_mm_get_sim_ready (MMModem *modem) { if (!modem) { g_debug ("%s: No Modem", __func__); pur_mm_state (PUR_MM_STATE_NO_MODEM); return; } mm_modem_get_sim (modem, NULL, (GAsyncReadyCallback)cb_get_sim_ready, NULL); } static void cb_sim_send_pin_ready (MMSim *sim, GAsyncResult *res, gpointer user_data) { g_autoptr(GError) error = NULL; if (!mm_sim_send_pin_finish (sim, res, &error)) { g_debug ("Couldn't unlock SIM - error: %s", error ? error->message : "unknown"); pur_mm_state (PUR_MM_STATE_MODEM_UNLOCK_ERROR); } else { pur_mm_purple_connect (); g_debug ("Succesfully unlocked %s", mm_sim_get_identifier (sim)); } } static void pur_mm_send_code_to_sim (const gchar *code) { pur_mm_data_t *mm_sms = pur_mm_get_data (); MMModemLock lock = mm_modem_get_unlock_required (mm_sms->modem); g_return_if_fail (code); if (lock == MM_MODEM_LOCK_SIM_PIN) { mm_sim_send_pin (mm_sms->sim, code, NULL, (GAsyncReadyCallback)cb_sim_send_pin_ready, NULL); } else { g_debug ("Unhandled lock type %u", lock); } } static void pur_mm_send_sms_to_purple_serv (PurSmsProps *sms_props) { PurpleConnection *pc; pur_mm_data_t *mm_sms = pur_mm_get_data (); if (pur_mm_get_modem_state ()) { pc = purple_account_get_connection (mm_sms->account); if (sms_props->message && sms_props->number && (sms_props->message[0] != '\0') && (sms_props->number [0] != '\0')) { serv_got_im (pc, sms_props->number, sms_props->message, PURPLE_MESSAGE_RECV, time (NULL)); if (mm_sms->sms_delete_received) { pur_mm_delete_sms (sms_props); } } } } static void pur_mm_get_sms_properties (PurSmsProps *sms_props) { MMSms *sms; const char *mm_time; struct tm t_8601; sms = sms_props->sms; sms_props->state = mm_sms_get_state (sms); sms_props->path = mm_sms_get_path (sms); sms_props->number = mm_sms_get_number (sms); sms_props->message = mm_sms_get_text (sms); mm_time = mm_sms_get_timestamp (sms); /* mm_time is set only for MM_SMS_PDU_TYPE_DELIVER or MM_SMS_PDU_TYPE_STATUS_REPORT PDUs */ if (mm_time) { strptime (mm_time, "%y%m%d%H%M%S%z", &t_8601); sms_props->t_stamp = mktime (&t_8601); } } static gboolean sms_props_match_by_msg_id (PurSmsProps *sms_props, gconstpointer message_ref) { return GPOINTER_TO_INT(message_ref) == sms_props->message_ref; } static void pur_mm_check_pdu_type (PurSmsProps *sms_props) { MMSms *sms; MMSmsState state; MMSmsPduType pdu_type; guint message_ref; guint delivery_state; guint index; pur_mm_data_t *mm_sms = pur_mm_get_data (); sms = sms_props->sms; state = mm_sms_get_state (sms); message_ref = mm_sms_get_message_reference (sms); pdu_type = mm_sms_get_pdu_type (sms); switch (pdu_type) { case MM_SMS_PDU_TYPE_SUBMIT: if (mm_sms_get_delivery_report_request (sms)) { sms_props->sms_id = mm_sms->sms_id; } else { if (mm_sms->sms_delete_sent) { pur_mm_delete_sms (sms_props); } } break; case MM_SMS_PDU_TYPE_CDMA_DELIVER: case MM_SMS_PDU_TYPE_DELIVER: if (state == MM_SMS_STATE_RECEIVED) { // The message has been completely received, // hand the SMS message to the libpurple core pur_mm_get_sms_properties (sms_props); pur_mm_send_sms_to_purple_serv (sms_props); } if (state == MM_SMS_STATE_RECEIVING) { // The first chunk of a multipart SMS has been // received -> wait for MM_SMS_STATE_RECEIVED g_signal_connect (sms_props->sms, "notify::state", G_CALLBACK (cb_sms_state_change), sms_props); } break; case MM_SMS_PDU_TYPE_STATUS_REPORT: // with a state < 4 the SMS has been successfully delivered delivery_state = mm_sms_get_delivery_state (sms); g_return_if_fail (mm_sms->sms_arr); if (message_ref && delivery_state <= 3) { if (g_ptr_array_find_with_equal_func (mm_sms->sms_arr, GINT_TO_POINTER(message_ref), (GEqualFunc)sms_props_match_by_msg_id, &index)) { pur_mm_signal_emit (sms_props->sms_id, SMS_RECEIPT_SMC_ACKN); // Delete the acknowledged sms g_ptr_array_remove_index_fast (mm_sms->sms_arr, index); } else { pur_mm_signal_emit (sms_props->sms_id, SMS_RECEIPT_SMC_NACK); } // Delete the delivery-receipt sms g_ptr_array_remove (mm_sms->sms_arr, sms_props); } break; case MM_SMS_PDU_TYPE_UNKNOWN: g_debug ("Unknown PDU type"); break; default: g_debug ("PDU type not handled"); } g_debug ("%s: pdu type %s", __func__, mm_sms_pdu_type_get_string (pdu_type)); } static void pur_mm_get_new_sms (char *path) { pur_mm_data_t *mm_sms = pur_mm_get_data (); mm_modem_messaging_list (mm_sms->modem_messaging, NULL, (GAsyncReadyCallback)cb_sms_list_new_ready, path); g_debug ("%s path %s", __func__, (char*)path); } static void pur_mm_get_all_sms (void) { pur_mm_data_t *mm_sms = pur_mm_get_data (); g_return_if_fail (MM_IS_MODEM_MESSAGING (mm_sms->modem_messaging)); mm_modem_messaging_list (mm_sms->modem_messaging, NULL, (GAsyncReadyCallback)cb_sms_list_all_ready, NULL); g_debug ("%s", __func__); } static void pur_mm_delete_sms (PurSmsProps *sms_props) { const char *path; pur_mm_data_t *mm_sms = pur_mm_get_data (); path = mm_sms_get_path (sms_props->sms); if (path) { mm_modem_messaging_delete (mm_sms->modem_messaging, path, NULL, (GAsyncReadyCallback)cb_sms_delete_finish, sms_props); } } static char * strip_phone_number (const char *number) { g_auto(GStrv) phone = NULL; if (!number || !*number) return NULL; phone = g_strsplit_set (number, "() -", 0); return g_strjoinv ("", phone); } static gboolean pur_mm_create_sms (const gchar *number, const gchar *message, guint valadity, gboolean delivery_report) { MMSmsProperties *properties; pur_mm_data_t *mm_sms = pur_mm_get_data (); properties = mm_sms_properties_new (); if (!pur_mm_get_modem_state ()) { return FALSE; } if ((message[0] != '\0') && (number[0] != '\0')) { g_autofree char *phone = NULL; phone = strip_phone_number (number); mm_sms_properties_set_text (properties, message); mm_sms_properties_set_number (properties, phone); mm_sms_properties_set_delivery_report_request (properties, delivery_report); if (valadity) { mm_sms_properties_set_validity_relative (properties, valadity); } } else { purple_debug_info ("mm-sms", "No SMS text or number provided"); g_debug ("No SMS text or number provided"); g_object_unref (properties); return FALSE; } g_debug ("Creating new SMS"); mm_modem_messaging_create (mm_sms->modem_messaging, properties, NULL, (GAsyncReadyCallback)cb_sms_create_finish, NULL); g_object_unref (properties); return TRUE; } static void pur_mm_init_modem (MMObject *obj) { MmGdbusModemMessaging *gdbus_sms; pur_mm_data_t *mm_sms = pur_mm_get_data (); mm_sms->object = obj; mm_sms->modem = mm_object_get_modem (MM_OBJECT(obj)); g_dbus_proxy_set_default_timeout (G_DBUS_PROXY(mm_sms->modem), PUR_MM_MODEM_TIMEOUT); mm_sms->modem_messaging = mm_object_get_modem_messaging (MM_OBJECT(obj)); g_return_if_fail (MM_IS_MODEM_MESSAGING (mm_sms->modem_messaging)); g_dbus_proxy_set_default_timeout (G_DBUS_PROXY(mm_sms->modem_messaging), PUR_MM_MODEM_SMS_TIMEOUT); gdbus_sms = MM_GDBUS_MODEM_MESSAGING(mm_sms->modem_messaging); g_signal_connect (gdbus_sms, "added", G_CALLBACK (cb_dbus_signal_sms_added), NULL); g_signal_connect (gdbus_sms, "deleted", G_CALLBACK (cb_dbus_signal_sms_deleted), NULL); g_debug ("%s", __func__); } static gboolean device_match_by_object (PurMmDevice *device, GDBusObject *object) { g_return_val_if_fail (G_IS_DBUS_OBJECT(object), FALSE); g_return_val_if_fail (MM_OBJECT(device->object), FALSE); return object == G_DBUS_OBJECT (device->object); } static void pur_mm_add_object (MMObject *obj) { PurMmDevice *device; const gchar *object_path; pur_mm_data_t *mm_sms = pur_mm_get_data (); object_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (obj)); g_return_if_fail (object_path); if (g_ptr_array_find_with_equal_func (mm_sms->device_arr, obj, (GEqualFunc)device_match_by_object, NULL)) { g_debug("Device %s already added", object_path); return; } g_debug ("Added device at: %s", object_path); // TODO choose default modem if devices->len > 1 device = g_new0 (PurMmDevice, 1); device->object = g_object_ref (MM_OBJECT (obj)); device->modem = mm_object_get_modem (MM_OBJECT(obj)); g_ptr_array_add (mm_sms->device_arr, device); pur_mm_init_modem (obj); pur_mm_get_all_sms (); pur_mm_state (PUR_MM_STATE_MODEM_FOUND); } static void cb_object_added (GDBusObjectManager *manager, GDBusObject *object, gpointer user_data) { MMModem *modem; modem = mm_object_get_modem (MM_OBJECT (object)); if (mm_object_peek_modem_messaging (MM_OBJECT (object))) purple_signal_emit (purple_plugins_get_handle (), "mm-sms-modem-added", (int)mm_modem_get_state (modem)); g_debug ("%s", __func__); } static void cb_object_removed (GDBusObjectManager *manager, GDBusObject *object, gpointer user_data) { guint index; pur_mm_data_t *mm_sms = pur_mm_get_data (); g_return_if_fail (G_IS_DBUS_OBJECT(object)); g_return_if_fail (G_IS_DBUS_OBJECT_MANAGER(manager)); if (g_ptr_array_find_with_equal_func (mm_sms->device_arr, object, (GEqualFunc)device_match_by_object, &index)) { g_ptr_array_remove_index_fast (mm_sms->device_arr, index); } if (MM_OBJECT(object) == mm_sms->object) { pur_mm_state (PUR_MM_STATE_NO_MODEM); } g_debug ("Modem removed: %s", g_dbus_object_get_object_path (object)); } static void cb_name_owner_changed (GDBusObjectManager *manager, GDBusObject *object, gpointer user_data) { gchar *name_owner; name_owner = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT (manager)); if (!name_owner) { pur_mm_state (PUR_MM_STATE_NO_MANAGER); } g_debug ("Name owner changed"); g_free (name_owner); } static void pur_mm_get_modems (void) { GList *list, *l; gboolean has_modem = FALSE; pur_mm_data_t *mm_sms = pur_mm_get_data (); g_return_if_fail (MM_IS_MANAGER (mm_sms->mm)); list = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (mm_sms->mm)); for (l = list; l != NULL; l = l->next) { if (!mm_object_peek_modem_messaging (l->data)) continue; has_modem = TRUE; pur_mm_add_object (MM_OBJECT(l->data)); } if (!has_modem) { pur_mm_state (PUR_MM_STATE_NO_MODEM); } else if (list) { g_list_free_full (list, g_object_unref); } } static void cb_mm_manager_new (GDBusConnection *connection, GAsyncResult *res, gpointer user_data) { gchar *name_owner; g_autoptr(GError) error = NULL; pur_mm_data_t *mm_sms = pur_mm_get_data (); mm_sms->mm = mm_manager_new_finish (res, &error); mm_sms->sms_arr = g_ptr_array_new (); mm_sms->device_arr = g_ptr_array_new_with_free_func ((GDestroyNotify) free_device); if (mm_sms->mm) { g_signal_connect (mm_sms->mm, "interface-added", G_CALLBACK (cb_object_added), NULL); g_signal_connect (mm_sms->mm, "object-added", G_CALLBACK (cb_object_added), NULL); g_signal_connect (mm_sms->mm, "object-removed", G_CALLBACK (cb_object_removed), NULL); g_signal_connect (mm_sms->mm, "notify::name-owner", G_CALLBACK (cb_name_owner_changed), NULL); name_owner = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT (mm_sms->mm)); purple_debug_info ("mm-sms", "ModemManager found: %s\n", name_owner); g_free (name_owner); pur_mm_get_modems (); pur_mm_state (PUR_MM_STATE_MANAGER_FOUND); } else { purple_debug_info ("mm-sms", "Error connecting to ModemManager: %s\n", error->message); g_debug ("Error connecting to ModemManager: %s", error->message); pur_mm_state (PUR_MM_STATE_NO_MANAGER); } } static void mm_appeared_cb (GDBusConnection *connection) { g_assert (G_IS_DBUS_CONNECTION (connection)); g_debug ("Modem Manager appeared"); mm_manager_new (connection, G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE, NULL, (GAsyncReadyCallback) cb_mm_manager_new, NULL); } static void mm_vanished_cb (GDBusConnection *connection) { g_assert (G_IS_DBUS_CONNECTION (connection)); g_debug ("Modem Manager vanished"); pur_mm_state (PUR_MM_STATE_NO_MANAGER); } static void pur_mm_get_modem_manager (void) { pur_mm_data_t *mm_sms = pur_mm_get_data (); mm_sms->mm_watch_id = g_bus_watch_name (G_BUS_TYPE_SYSTEM, MM_DBUS_SERVICE, G_BUS_NAME_WATCHER_FLAGS_AUTO_START, (GBusNameAppearedCallback)mm_appeared_cb, (GBusNameVanishedCallback)mm_vanished_cb, NULL, NULL); } static void pur_mm_signal_emit (gchar *sms_id, int state) { pur_mm_data_t *mm_sms = pur_mm_get_data (); purple_signal_emit (purple_conversations_get_handle (), "sms-sent", sms_id, state); g_clear_pointer (&mm_sms->sms_id, g_free); } static const char * pur_mm_list_icon (PurpleAccount *account, PurpleBuddy *buddy) { return "mm-sms"; } static const gchar * pur_mm_list_emblem (PurpleBuddy *buddy) { return "mobile"; } GList * pur_mm_status_types (PurpleAccount *account) { GList *types = NULL; PurpleStatusType *status; status = purple_status_type_new_full (PURPLE_STATUS_AVAILABLE, NULL, "Online", TRUE, TRUE, FALSE); types = g_list_append (types, status); status = purple_status_type_new_full (PURPLE_STATUS_MOBILE, "mobile", "Online", FALSE, FALSE, TRUE); types = g_list_append (types, status); status = purple_status_type_new_full (PURPLE_STATUS_OFFLINE, NULL, "Offline", FALSE, FALSE, FALSE); types = g_list_append (types, status); return types; } static void pur_mm_add_buddy (PurpleConnection *pc, PurpleBuddy *buddy, PurpleGroup *group) { const gchar *buddy_name; pur_mm_data_t *mm_sms = pur_mm_get_data (); buddy_name = purple_buddy_get_name (buddy); purple_prpl_got_user_status (mm_sms->account, buddy_name, "available", NULL); } static void pur_mm_set_buddy_status (void) { const gchar *buddy_name; pur_mm_data_t *mm_sms = pur_mm_get_data (); PurpleBlistNode *node = purple_blist_get_root (); while (node) { if (PURPLE_BLIST_NODE_IS_BUDDY(node) && purple_buddy_get_account ((PurpleBuddy *)node) == mm_sms->account) { buddy_name = purple_buddy_get_name ((PurpleBuddy *)node); purple_prpl_got_user_status (mm_sms->account, buddy_name, "available", NULL); } node = purple_blist_node_next (node, FALSE); } } static void pur_mm_disconnect (void) { pur_mm_data_t *mm_sms = pur_mm_get_data (); if (mm_sms->device_arr && mm_sms->device_arr->len) { g_ptr_array_set_size (mm_sms->device_arr, 0); g_ptr_array_unref(mm_sms->device_arr); } if (mm_sms->sms_arr && mm_sms->sms_arr->len) { g_ptr_array_unref(mm_sms->sms_arr); } mm_sms->modem_available = FALSE; } static void pur_mm_purple_connect (void) { PurpleConnection *pc; pur_mm_data_t *mm_sms = pur_mm_get_data (); if (!pur_mm_get_modem_state ()) { return; } pc = purple_account_get_connection (mm_sms->account); purple_connection_update_progress (pc, "Connected", 1, 2); purple_blist_add_account (mm_sms->account); purple_connection_set_state (pc, PURPLE_CONNECTED); pur_mm_set_buddy_status (); pur_mm_state (PUR_MM_STATE_READY); } static void pur_mm_state (int state) { PurpleConnection *pc; MMModemState modem_state; const char *pin = NULL; pur_mm_data_t *mm_sms = pur_mm_get_data (); pc = purple_account_get_connection (mm_sms->account); purple_signal_emit (purple_plugins_get_handle (), "mm-sms-state", state); /* This can happen if ModemManager has never run */ if (!pc) return; switch (state) { case PUR_MM_STATE_MODEM_FOUND: if (!mm_sms->modem_available) { if (mm_sms->modem) { modem_state = mm_modem_get_state (mm_sms->modem); if (modem_state == MM_MODEM_STATE_LOCKED) { pin = purple_account_get_password (mm_sms->account); pur_mm_get_sim_ready (mm_sms->modem); pur_mm_send_code_to_sim (pin); } else { mm_modem_get_sim (mm_sms->modem, NULL, (GAsyncReadyCallback)cb_get_sim_ready, NULL); pur_mm_purple_connect (); } } mm_sms->modem_available = TRUE; } g_debug ("PUR_MM_STATE_MODEM_FOUND"); break; case PUR_MM_STATE_NO_MODEM: if (mm_sms->modem_available) { pur_mm_disconnect (); purple_connection_error_reason (pc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, "Modem vanished"); } else { purple_connection_error_reason (pc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, "Could not connect to modem"); } mm_sms->modem_available = FALSE; g_debug ("PUR_MM_STATE_NO_MODEM"); break; case PUR_MM_STATE_NO_MESSAGING_MODEM: if (mm_sms->modem_available) { pur_mm_disconnect (); purple_connection_error_reason (pc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, "Modem has no messaging capabilities"); } mm_sms->modem_available = FALSE; g_debug ("PUR_MM_STATE_NO_MESSAGING_MODEM"); break; case PUR_MM_STATE_MODEM_DISABLED: purple_connection_error_reason (pc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, "Modem disabled"); mm_sms->modem_available = FALSE; g_debug ("PUR_MM_STATE_MODEM_DISABLED"); break; case PUR_MM_STATE_MANAGER_FOUND: if (!mm_sms->manager_available) { mm_sms->manager_available = TRUE; } g_debug ("PUR_MM_STATE_MANAGER_FOUND"); break; case PUR_MM_STATE_NO_MANAGER: if (mm_sms->manager_available) { pur_mm_disconnect (); g_clear_object (&mm_sms->mm); purple_connection_error_reason (pc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, "ModemManager vanished"); } else { purple_connection_error_reason (pc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, "Could not connect to ModemManager"); } mm_sms->manager_available = FALSE; g_debug ("PUR_MM_STATE_NO_MANAGER"); break; case PUR_MM_STATE_MODEM_UNLOCK_ERROR: purple_connection_error_reason (pc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, "SIM card unlock failed"); g_debug ("PUR_MM_STATE_MODEM_UNLOCK_ERROR"); break; case PUR_MM_STATE_READY: g_debug ("PUR_MM_STATE_READY"); break; default: g_return_if_reached (); } } static void pur_mm_login (PurpleAccount *account) { PurpleConnection *pc; pur_mm_data_t *mm_sms = pur_mm_get_data (); pc = purple_account_get_connection (account); mm_sms->manager_available = FALSE; mm_sms->modem_available = FALSE; mm_sms->account = account; mm_sms->sms_delivery_report = purple_account_get_bool (account, "sms_delivery_report", TRUE); mm_sms->sms_delete_sent = purple_account_get_bool (account, "sms_delete_sent", TRUE); mm_sms->sms_delete_received = purple_account_get_bool (account, "sms_delete_received", TRUE); mm_sms->sms_validity = purple_account_get_int (account, "sms_validity", SMS_VALIDITY_NOT_SET); purple_connection_update_progress (pc, "Connecting to modem...", 0, 2); purple_connection_set_state (pc, PURPLE_CONNECTING); pur_mm_get_modem_manager (); } static void pur_mm_close (PurpleConnection *pc) { pur_mm_disconnect (); purple_connection_set_protocol_data (pc, NULL); purple_connection_set_state (pc, PURPLE_DISCONNECTED); } static int pur_mm_send_im (PurpleConnection *pc, const char *who, const char *message, PurpleMessageFlags flags) { pur_mm_data_t *mm_sms = pur_mm_get_data (); if (flags & PURPLE_MESSAGE_NOTIFY) { mm_sms->sms_id = g_strdup (message); return 1; } if (pur_mm_create_sms (who, message, mm_sms->sms_validity, mm_sms->sms_delivery_report)) { return 1; } else { return -1; } return 1; } static gboolean plugin_load (PurplePlugin * plugin) { return TRUE; } static gboolean plugin_unload (PurplePlugin *plugin) { return TRUE; } static void plugin_destroy (PurplePlugin *plugin) { pur_mm_data_t *mm_sms = pur_mm_get_data (); g_clear_handle_id (&mm_sms->mm_watch_id, g_bus_unwatch_name); purple_debug_info ("mm-sms", "shutting down\n"); g_debug ("Shutting down"); } static gboolean plugin_can_receive_file (PurpleConnection *gc, const char *who) { return FALSE; } static gboolean plugin_offline_message (const PurpleBuddy *buddy) { return TRUE; } static GHashTable * pur_mm_account_text_table (PurpleAccount *account) { GHashTable *table; table = g_hash_table_new (g_str_hash, g_str_equal); g_hash_table_insert (table, (gpointer)"login_label", (gpointer)_("Anything")); g_hash_table_insert (table, (gpointer)"password_label", (gpointer)_("PIN Number")); return table; } static void init_plugin (PurplePlugin * plugin) { PurpleAccountOption *option; PurplePluginInfo *info = plugin->info; PurplePluginProtocolInfo *prpl_info = info->extra_info; PurpleKeyValuePair *kvp; GList *list = NULL; pur_mm_data_t *mm_sms = pur_mm_get_data (); memset (mm_sms, 0, sizeof (pur_mm_data_t)); purple_debug_info ("mm-sms", "starting up\n"); mm_sms->sms_validity = 0; mm_sms->sms_delete_sent = TRUE; mm_sms->sms_delete_received = TRUE; mm_sms->sms_delivery_report = TRUE; #define ADD_OPTION(list, desc, v) { \ kvp = g_new0 (PurpleKeyValuePair, 1); \ kvp->key = g_strdup ((desc)); \ kvp->value = g_strdup_printf ("%d", v); \ list = g_list_prepend (list, kvp); \ } ADD_OPTION(list, _("30 days"), SMS_VALIDITY_30_DAYS); ADD_OPTION(list, _("7 days"), SMS_VALIDITY_7_DAYS); ADD_OPTION(list, _("2 days"), SMS_VALIDITY_2_DAYS); ADD_OPTION(list, _("SMC Default"), SMS_VALIDITY_NOT_SET); option = purple_account_option_list_new (_("SMS validity period"), "validity", list); prpl_info->protocol_options = g_list_append (prpl_info->protocol_options, option); option = purple_account_option_bool_new (_("Request delivery reports from SMC"), "sms_delivery_report", TRUE); prpl_info->protocol_options = g_list_append (prpl_info->protocol_options, option); option = purple_account_option_bool_new (_("Remove sent SMS from modem storage"), "sms_delete_sent", TRUE); prpl_info->protocol_options = g_list_append (prpl_info->protocol_options, option); option = purple_account_option_bool_new (_("Remove received SMS from modem storage"), "sms_delete_received", TRUE); prpl_info->protocol_options = g_list_append (prpl_info->protocol_options, option); purple_signal_register (purple_plugins_get_handle (), "mm-sms-modem-added", purple_marshal_VOID__INT, NULL, 1, purple_value_new (PURPLE_TYPE_INT)); purple_signal_register (purple_plugins_get_handle (), "mm-sms-state", purple_marshal_VOID__INT, NULL, 1, purple_value_new (PURPLE_TYPE_INT)); purple_signal_register (purple_plugins_get_handle (), "mm-sms-country-code", purple_marshal_VOID__POINTER, NULL, 1, purple_value_new (PURPLE_TYPE_STRING)); purple_signal_register (purple_conversations_get_handle (), "sms-sent", purple_marshal_VOID__POINTER_UINT, NULL, 2, purple_value_new (PURPLE_TYPE_STRING), purple_value_new (PURPLE_TYPE_INT)); purple_cmd_register ("mm-sms", "ww", PURPLE_CMD_P_PLUGIN, PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, NULL, cb_mm_sms_cmd, "mm-sms <help>: " "For a list of commands use the 'help' argument.", NULL); } static PurplePluginProtocolInfo prpl_info = { /* options */ OPT_PROTO_PASSWORD_OPTIONAL | OPT_PROTO_IM_IMAGE, NULL, /* user_splits */ NULL, /* protocol_options */ { /* icon_spec, a PurpleBuddyIconSpec */ "png,jpg,gif", /* format */ 0, /* min_width */ 0, /* min_height */ 128, /* max_width */ 128, /* max_height */ 10000, /* max_filesize */ PURPLE_ICON_SCALE_DISPLAY, /* scale_rules */ }, /* icon_spec */ pur_mm_list_icon, /* list_icon */ pur_mm_list_emblem, /* list_emblems */ NULL, /* status_text */ NULL, /* tooltip_text */ pur_mm_status_types, /* status_types */ NULL, /* blist_node_menu */ NULL, /* chat_info */ NULL, /* chat_info_defaults */ pur_mm_login, /* login */ pur_mm_close, /* close */ pur_mm_send_im, /* send_im */ NULL, /* set_info */ NULL, /* send_typing */ NULL, /* get_info */ NULL, /* set_status */ NULL, /* set_idle */ NULL, /* change_passwd */ pur_mm_add_buddy, /* add_buddy */ NULL, /* add_buddies */ NULL, /* remove_buddy */ NULL, /* remove_buddies */ NULL, /* add_permit */ NULL, /* add_deny */ NULL, /* rem_permit */ NULL, /* rem_deny */ NULL, /* set_permit_deny */ NULL, /* join_chat */ NULL, /* reject chat invite */ NULL, /* get_chat_name */ NULL, /* chat_invite */ NULL, /* chat_leave */ NULL, /* chat_whisper */ NULL, /* chat_send */ NULL, /* keepalive */ NULL, /* register_user */ NULL, /* get_cb_info */ NULL, /* get_cb_away */ NULL, /* alias_buddy */ NULL, /* group_buddy */ NULL, /* rename_group */ NULL, /* buddy_free */ NULL, /* convo_closed */ purple_normalize_nocase,/* normalize */ NULL, /* set_buddy_icon */ NULL, /* remove_group */ NULL, /* get_cb_real_name */ NULL, /* set_chat_topic */ NULL, /* find_blist_chat */ NULL, /* roomlist_get_list */ NULL, /* roomlist_cancel */ NULL, /* roomlist_expand_category */ plugin_can_receive_file, /* can_receive_file */ NULL, /* send_file */ NULL, /* new_xfer */ plugin_offline_message, /* offline_message */ NULL, /* whiteboard_prpl_ops */ NULL, /* send_raw */ NULL, /* roomlist_room_serialize */ NULL, /* unregister_user */ NULL, /* send_attention */ NULL, /* attention_types */ sizeof(PurplePluginProtocolInfo), /* struct_size */ pur_mm_account_text_table, /* get_account_text_table */ NULL, // initiate_media NULL, // get_media_caps NULL, // get_moods NULL, // set_public_alias NULL, // get_public_alias NULL, // add_buddy_with_invite NULL // add_buddies_with_invite }; static PurplePluginInfo info = { PURPLE_PLUGIN_MAGIC, PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION, PURPLE_PLUGIN_PROTOCOL, NULL, 0, NULL, PURPLE_PRIORITY_DEFAULT, "prpl-mm-sms", "ModemManager SMS", PUR_MM_SMS_PLUGIN_VERSION, "Modemmanager Protocol Plugin", "Modemmanager SMS Plugin", "Andrea Schäfer ", "https://source.puri.sm/Librem5/purple-mm-sms", plugin_load, plugin_unload, plugin_destroy, NULL, &prpl_info, NULL, NULL, NULL, NULL, NULL, NULL }; PURPLE_INIT_PLUGIN (mm_sms, init_plugin, info) purple-mm-sms-0.1.7/mm-sms.h000066400000000000000000000022531371145551200156250ustar00rootroot00000000000000#ifndef __MM_H_INCLUDE__ #define __MM_H_INCLUDE__ typedef struct { PurpleAccount *account; MMManager *mm; MMObject *object; MMModem *modem; MMSim *sim; MMModemMessaging *modem_messaging; GPtrArray *sms_arr; GPtrArray *device_arr; gboolean modem_available; gboolean manager_available; gchar *sms_id; guint sms_validity; gboolean sms_delete_sent; gboolean sms_delete_received; gboolean sms_delivery_report; guint lock_type; guint mm_watch_id; } pur_mm_data_t; pur_mm_data_t *pur_mm_get_data (void); enum { PUR_MM_STATE_NO_MANAGER, PUR_MM_STATE_MANAGER_FOUND, PUR_MM_STATE_NO_MODEM, PUR_MM_STATE_MODEM_FOUND, PUR_MM_STATE_NO_MESSAGING_MODEM, PUR_MM_STATE_MODEM_DISABLED, PUR_MM_STATE_MODEM_UNLOCK_ERROR, PUR_MM_STATE_READY } e_purple_connection; enum { SMS_VADILITY_SMC_DEFAULT, SMS_VADILITY_24_HOURS, SMS_VADILITY_7_DAYS } e_sms_vadility; enum { SMS_RECEIPT_NONE = -1, SMS_RECEIPT_MM_ACKN = 0, SMS_RECEIPT_SMC_ACKN, SMS_RECEIPT_SMC_NACK } e_sms_receipt_states; #endif