Commit 25e2163bbde1d7bcfb57a2f15ce81fdb5eb96c42

Authored by Matti Virkkunen
2 parents 4c30a21a 26f65110

Merge branch 'master' into libpurple3-backports

Makefile
... ... @@ -6,7 +6,7 @@ clean:
6 6 $(MAKE) -C libpurple clean
7 7  
8 8 .PHONY: user-install
9   -user-install: all
  9 +user-install:
10 10 $(MAKE) -C libpurple user-install
11 11  
12 12 .PHONY: user-uninstall
... ...
README.md
... ... @@ -5,23 +5,75 @@ libpurple (Pidgin, Finch) protocol plugin for [LINE](http://line.me/).
5 5  
6 6 ![Screenshot](http://i.imgur.com/By1yLXB.png)
7 7  
8   -Where are the binaries and packages?
9   -------------------------------------
  8 +Install via package manager (Ubuntu/Debian)
  9 +-------------------------------------------
10 10  
11   -I am not looking into "easy to install" options before I'm satisfied with the stability. I'd rather
12   -not have people who cannot figure out how to compile software by themselves be disappointed by an
13   -unstable plugin.
  11 +An APT repository is available for installing the plugin. The repository contains the purple-line
  12 +package itself, and the required Apache Thrift packages which are not properly packaged by either
  13 +distribution.
14 14  
15   -How to install
16   ---------------
  15 +* http://debian.altrepo.eu/ (main)
  16 +* http://debian.surlinter.net/ (mirror)
17 17  
18   -Make sure you have the required prerequisites:
  18 +For instructions for adding a custom repository on Ubuntu, see
  19 +[the Ubuntu wiki](https://help.ubuntu.com/community/Repositories/Ubuntu).
  20 +
  21 +For Debian, see [the Debian wiki](https://www.debian.org/releases/), or just add the following line
  22 +to your `sources.list` file:
  23 +
  24 + deb http://debian.altrepo.eu/ code_name main
  25 +
  26 +Replace code_name with your distribution specific codename. The codenames are listed in the
  27 +[debian.altrepo.eu wiki](http://altrepo.eu/git/debian.altrepo.eu/wikis/home#note).
  28 +In order to validate package signatures you need to add
  29 +[this public key](http://debian.altrepo.eu/altrepo_eu.pub) to the APT key list. It can be done
  30 +using the following command:
  31 +
  32 + wget -qO - http://debian.altrepo.eu/altrepo_eu.pub | sudo apt-key add -
  33 +
  34 +On either distribution, after adding the repository and key, run the following commands to install
  35 +the plugin:
  36 +
  37 + sudo apt-get update
  38 + sudo apt-get install purple-line
  39 +
  40 +Install from source (Ubuntu/Debian)
  41 +-----------------------------------
  42 +
  43 +apt-build enables you to install from source easily. You need to add a source entry to
  44 +/etc/apt/sources.list. Run the following commands as root to install from source:
  45 +
  46 + apt-get install apt-build
  47 + echo "deb-src http://debian.altrepo.eu/ code_name main" >> /etc/apt/sources.list
  48 + echo "deb http://debian.altrepo.eu/ code_name main" >> /etc/apt/sources.list
  49 + apt-get update
  50 + apt-build -y install purple-line
  51 +
  52 +Note that apt-build is not an official APT family program. If you install via source, your
  53 +purple-line version will be up to date with the git repository. Note that the current git version
  54 +may not have been tested on Ubuntu/Debian. The package will install known build dependencies, but if
  55 +the git version requires new dependencies, compilation will fail.
  56 +
  57 +Install from source (Arch Linux)
  58 +--------------------------------
  59 +
  60 +Arch Linux packages all the required dependencies and there's a PKGBUILD available (not yet in AUR).
  61 +You can install the plugin by simply typing:
  62 +
  63 + sudo pacman -S base-devel
  64 + curl -O http://altrepo.eu/git/arch.altrepo.eu/raw/master/purple-line/PKGBUILD
  65 + makepkg -s -i -c
  66 +
  67 +Install from source (any distribution)
  68 +--------------------------------------
  69 +
  70 +Make sure you have the required prerequisites installed:
19 71  
20 72 * libpurple - Library that provides the core functionality of Pidgin and other compatible clients.
21   - Probably available from your package manager
22   -* zlib - Probably already installed
23   -* thrift / libthrift - Apache Thrift compiler and C++ library. May be available from your package
24   - manager.
  73 + Probably available from your package manager.
  74 +* thrift - Apache Thrift compiler. May be available from your package manager.
  75 +* libthrift - Apache Thrift C++ library. May be available from your package manager.
  76 +* libgcrypt - Crypto library. Probably available from your package manager.
25 77  
26 78 To install the plugin system-wide, run:
27 79  
... ... @@ -34,33 +86,8 @@ distributions that do not package Thrift.
34 86  
35 87 You can also install the plugin for your user only by replacing `install` with `user-install`.
36 88  
37   -How to install (Arch Linux)
38   ----------------------------
39   -
40   -Arch Linux packages all the required dependencies, so you can install the plugin by simply typing:
41   -
42   - sudo pacman -S thrift libpurple
43   - make
44   - sudo make install
45   -
46   -How to install (Ubuntu)
47   ------------------------
48   -
49   -Ubuntu does not currently package Thrift so it must be obtained elsewhere, or statically linked. To
50   -build the plugin with a statically linked Thrift library, type:
51   -
52   - sudo apt-get install \
53   - libpurple-dev \
54   - libboost-dev libboost-test-dev \
55   - libboost-program-options-dev libboost-system-dev libboost-filesystem-dev libevent-dev \
56   - automake libtool flex bison pkg-config g++ libssl-dev
57   - make THRIFT_STATIC=true
58   - sudo make install THRIFT_STATIC=true
59   -
60   -The installed packages include all the suggested dependencies from Thrift's website.
61   -
62   -Implemented
63   ------------
  89 +Features implemented
  90 +--------------------
64 91  
65 92 * Logging in
66 93 * Authentication
... ... @@ -92,8 +119,8 @@ Implemented
92 119 * Audio (send/receive)
93 120 * Location (receive)
94 121  
95   -To do
96   ------
  122 +Features not yet implemented
  123 +----------------------------
97 124  
98 125 * Only fetch unseen messages, let a log plugin handle already seen messages
99 126 * Implement timeouts for faster reconnections
... ...
debian/changelog 0 → 100644
  1 +purple-line (0.1.2-ubuntu2) vivid; urgency=low
  2 +
  3 + * Internal LINE version updated.Fix new thrift bug.
  4 +
  5 + -- Akira Nakagawa <matyapiro31@gmail.com> Wed, 28 Oct 2015 20:30:00 +0900
  6 +purple-line (0.1.1-ubuntu2) vivid; urgency=low
  7 +
  8 + * Support new LINE login.
  9 +
  10 + -- Akira Nakagawa <matyapiro31@gmail.com> Thu, 17 Sep 2015 21:04:00 +0900
  11 +purple-line (0.1-ubuntu1) vivid; urgency=low
  12 +
  13 + * Release for public.
  14 +
  15 + -- Akira Nakagawa <matyapiro31@gmail.com> Tue, 28 Jul 2015 13:42:00 +0900
  16 +purple-line (0.0-ubuntu1) trusty; urgency=low
  17 +
  18 + * Initial Release
  19 +
  20 + -- Akira Nakagawa <matyapiro31@gmail.com> Sat, 25 Jul 2015 21:31:57 +0900
  21 +
... ...
debian/compat 0 → 100755
  1 +8
... ...
debian/control 0 → 100644
  1 +Source: purple-line
  2 +Section: contrib/net
  3 +Priority: optional
  4 +Maintainer: Matti Virkkunen <mvirkkunen@gmail.com>
  5 +Build-Depends: debhelper (>= 9),thrift-compiler,libpurple-dev,libthrift-dev,libgcrypt11-dev,libgpg-error-dev,libglib2.0-dev,python,dh-python,quilt
  6 +Standards-Version: 3.9.6
  7 +Homepage: http://altrepo.eu/git/purple-line/
  8 +Vcs-Git: http://altrepo.eu/git/purple-line.git
  9 +
  10 +Package: purple-line
  11 +Architecture: any
  12 +Depends: ${misc:Depends},${shlibs:Depends},
  13 + libpurple0,libthrift0,libgcrypt11|libgcrypt20,libgpg-error0
  14 +Provides: purple-line
  15 +Description: libpurple (Pidgin, Finch) protocol plugin for LINE
  16 + Supported Application:Pidgin Finch
  17 + Unsuppoted Application:Empathy
  18 + This plugin provides most features of LINE,
  19 + but some of them are not yet implemented now.
  20 + For example,buddy list configuration, account icon modification,
  21 + to get message is read or not,VoIP receiving/calling.
  22 + If you want to know more details of this plugin,
  23 + please visit http://altrepo.eu/git/purple-line/
  24 + .
  25 +
  26 +Package: purple-line-dbg
  27 +Section: contrib/debug
  28 +Architecture: any
  29 +Priority: extra
  30 +Depends: ${misc:Depends}, purple-line (= ${binary:Version})
  31 +Description: libpurple (Pidgin, Finch) protocol plugin for LINE
  32 + Supported Application:Pidgin Finch
  33 + Unsuppoted Application:Empathy
  34 + This plugin provides most features of LINE,
  35 + but some of them are not yet implemented now.
  36 + For example,buddy list configuration, account icon modification,
  37 + to get message is read or not,VoIP receiving/calling.
  38 + If you want to know more details of this plugin,
  39 + please visit http://altrepo.eu/git/purple-line/
  40 + This package includes debug symbols.
  41 + .
... ...
debian/copyright 0 → 100644
  1 +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
  2 +Upstream-Name: purple-line
  3 +Source: http://altrepo.eu/git/purple-line/
  4 +
  5 +Files: *
  6 +Copyright: Copyright (c) 2014, 2015 Matti Virkkunen
  7 +License: MIT
  8 + Permission is hereby granted, free of charge, to any person obtaining
  9 + a copy of this software and associated documentation files (the
  10 + "Software"), to deal in the Software without restriction, including
  11 + without limitation the rights to use, copy, modify, merge, publish,
  12 + distribute, sublicense, and/or sell copies of the Software, and to
  13 + permit persons to whom the Software is furnished to do so, subject to
  14 + the following conditions:
  15 + .
  16 + The above copyright notice and this permission notice shall be included
  17 + in all copies or substantial portions of the Software.
  18 + .
  19 + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  20 + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  21 + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  22 + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
  23 + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  24 + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  25 + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
... ...
debian/docs 0 → 100644
  1 +README.md
... ...
debian/patches/change-version.patch 0 → 100755
  1 +Change version id to 0.1-ubuntu1
  2 +Index: purple-line/libpurple/pluginmain.cpp
  3 +===================================================================
  4 +--- purple-line.orig/libpurple/pluginmain.cpp
  5 ++++ purple-line/libpurple/pluginmain.cpp
  6 +@@ -34,7 +34,7 @@ static void init_info(PurplePluginInfo &
  7 +
  8 + i.id = (char *)LINE_PRPL_ID;
  9 + i.name = (char *)"LINE";
  10 +- i.version = (char *)"0.1";
  11 ++ i.version = (char *)"0.1.2-ubuntu2";
  12 + i.summary = (char *)"Plugin for Naver LINE";
  13 + i.description = (char *)"Plugin for Naver LINE";
  14 + i.author = (char *)"Matti Virkkunen <mvirkkunen@gmail.com>";
... ...
debian/patches/fix-build-action.patch 0 → 100755
  1 +add to /usr/lib/debug debug symboled file.
  2 +remove strip.
  3 +Index: purple-line/libpurple/Makefile
  4 +===================================================================
  5 +--- purple-line.orig/libpurple/Makefile
  6 ++++ purple-line/libpurple/Makefile
  7 +@@ -38,7 +38,6 @@ all: $(MAIN)
  8 +
  9 + $(MAIN): $(OBJS) $(THRIFT_DEP)
  10 + $(CXX) $(CXXFLAGS) -Wl,-z,defs -o $(MAIN) $(OBJS) $(LIBS)
  11 +- strip $(MAIN)
  12 +
  13 + .cpp.o:
  14 + $(CXX) $(CXXFLAGS) -std=c++11 -c $< -o $@
  15 +@@ -79,7 +78,8 @@ user-uninstall:
  16 +
  17 + .PHONY: install
  18 + install: all
  19 +- install -D $(MAIN) $(DESTDIR)$(PURPLE_PLUGIN_DIR)/$(MAIN)
  20 ++ install -m 664 -D $(MAIN) $(DESTDIR)/usr/lib/debug$(PURPLE_PLUGIN_DIR)/$(MAIN)
  21 ++ install -m 664 -D $(MAIN) $(DESTDIR)$(PURPLE_PLUGIN_DIR)/$(MAIN)
  22 + install -m 644 -D ../icons/16/line.png $(DESTDIR)$(PURPLE_DATA_ROOT_DIR)/pixmaps/pidgin/protocols/16/line.png
  23 + install -m 644 -D ../icons/22/line.png $(DESTDIR)$(PURPLE_DATA_ROOT_DIR)/pixmaps/pidgin/protocols/22/line.png
  24 + install -m 644 -D ../icons/48/line.png $(DESTDIR)$(PURPLE_DATA_ROOT_DIR)/pixmaps/pidgin/protocols/48/line.png
... ...
debian/patches/series 0 → 100755
  1 +change-version.patch
  2 +fix-build-action.patch
... ...
debian/purple-line-dbg.install 0 → 100644
  1 +usr/lib/debug/usr/lib/purple-2/libline.so
... ...
debian/purple-line.install 0 → 100644
  1 +usr/lib/purple-2/libline.so
  2 +usr/share/pixmaps/
... ...
debian/rules 0 → 100755
  1 +#!/usr/bin/make -f
  2 +%:
  3 + dh $@
  4 +
  5 +override_dh_auto_build:
  6 + $(MAKE) all
  7 +
  8 +override_dh_strip:
  9 + dh_strip --dbg-package=purple-line-dbg
  10 +
  11 +override_dh_builddeb:
  12 + dh_builddeb -- -Zxz
  13 +
  14 +override_dh_shlibdeps:
  15 + dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info
  16 +
  17 +#comment out command if you don't want to build clean every time.
  18 +override_dh_auto_clean:
  19 + $(MAKE) clean
  20 + dh_clean
  21 +
  22 +override_dh_pysupport:
  23 + dh_python2
... ...
debian/source/format 0 → 100755
  1 +3.0 (quilt)
... ...
libpurple/Makefile
... ... @@ -6,17 +6,21 @@ ifdef THRIFT_STATIC
6 6 THRIFT_LIBS = -L$(THRIFT_STATIC_DIR)/lib/cpp/.libs -Wl,-Bstatic -lthrift -Wl,-Bdynamic
7 7 THRIFT_DEP = $(THRIFT)
8 8 else
9   - THRIFT = thrift
  9 + THRIFT ?= thrift
10 10 THRIFT_CXXFLAGS = `pkg-config --cflags thrift`
11 11 THRIFT_LIBS = `pkg-config --libs thrift`
12 12 endif
13 13  
14   -CXX = g++
15   -CXXFLAGS = -g -std=c++11 -Wall -shared -fPIC \
  14 +CXX ?= g++
  15 +CXXFLAGS = -g -Wall -shared -fPIC \
16 16 -DHAVE_INTTYPES_H -DHAVE_CONFIG_H -DPURPLE_PLUGINS \
17   - `pkg-config --cflags purple` -I./backports $(THRIFT_CXXFLAGS)
  17 + `pkg-config --cflags purple` `libgcrypt-config --cflags` `gpg-error-config --cflags` \
  18 + -I./backports \
  19 + $(THRIFT_CXXFLAGS)
18 20  
19   -LIBS = `pkg-config --libs purple zlib` -L./backports -lpurplebackports $(THRIFT_LIBS)
  21 +LIBS = `pkg-config --libs purple zlib` `libgcrypt-config --libs` `gpg-error-config --libs` \
  22 + -L./backports -lpurplebackports \
  23 + $(THRIFT_LIBS)
20 24  
21 25 PURPLE_PLUGIN_DIR:=$(shell pkg-config --variable=plugindir purple)
22 26 PURPLE_DATA_ROOT_DIR:=$(shell pkg-config --variable=datarootdir purple)
... ... @@ -41,15 +45,19 @@ $(MAIN): $(OBJS) backports/libpurplebackports.a $(THRIFT_DEP)
41 45 strip $(MAIN)
42 46  
43 47 .cpp.o:
44   - $(CXX) $(CXXFLAGS) -c $< -o $@
  48 + $(CXX) $(CXXFLAGS) -std=c++11 -c $< -o $@
45 49  
46 50 backports/libpurplebackports.a:
47 51 $(MAKE) -C backports
48 52  
49   -thrift_line: line.thrift $(THRIFT_DEP)
  53 +# The Thrift generator generates three files at once, this file shall represent them.
  54 +thrift_line/TalkService.cpp: line.thrift $(THRIFT_DEP) $@
50 55 mkdir -p thrift_line
51 56 $(THRIFT) --gen cpp -out thrift_line line.thrift
52 57  
  58 +# If the representative file exists, the others should too.
  59 +thrift_line/line_types.cpp thrift_line/line_constants.cpp: thrift_line/TalkService.cpp
  60 +
53 61 $(THRIFT):
54 62 mkdir -p $(THRIFT_STATIC_DIR)
55 63 wget -P $(THRIFT_STATIC_DIR) \
... ... @@ -67,6 +75,7 @@ $(THRIFT):
67 75  
68 76 .PHONY: clean
69 77 clean:
  78 + rm -f .depend
70 79 rm -f $(MAIN)
71 80 rm -f *.o
72 81 rm -rf thrift_line
... ... @@ -99,18 +108,11 @@ uninstall:
99 108  
100 109 depend: .depend
101 110  
102   -.depend: thrift_line $(REAL_SRCS)
103   - rm -f .depend
104   - $(CXX) $(CXXFLAGS) -MM $(REAL_SRCS) >>.depend
  111 +.depend: $(SRCS)
  112 + $(CXX) $(CXXFLAGS) -MM $(REAL_SRCS) >.depend
105 113  
106 114 ifneq ($(MAKECMDGOALS),clean)
107   -ifneq ($(MAKECMDGOALS),user-install)
108   -ifneq ($(MAKECMDGOALS),user-uninstall)
109   -ifneq ($(MAKECMDGOALS),install)
110 115 ifneq ($(MAKECMDGOALS),uninstall)
111 116 -include .depend
112 117 endif
113 118 endif
114   -endif
115   -endif
116   -endif
... ...
libpurple/constants.hpp
1 1 #pragma once
2 2  
3   -#define LINE_PRPL_ID "prpl-mvirkkunen-line"
  3 +#define LINE_PRPL_ID "prpl-line"
4 4  
5 5 #define LINE_TALK_URL "https://gd2.line.naver.jp/"
6 6 #define LINE_OS_URL "https://os.line.naver.jp/"
... ... @@ -13,7 +13,7 @@
13 13 #define LINE_SHOP_PATH "SHOP4"
14 14  
15 15 #define LINE_USER_AGENT "purple-line (LINE for libpurple/Pidgin)"
16   -#define LINE_APPLICATION "DESKTOPWIN\t3.2.1.83\tWINDOWS\t5.1.2600-XP-x64"
  16 +#define LINE_APPLICATION "DESKTOPWIN\t4.1.3.586\tWINDOWS\t5.1.2600-XP-x64"
17 17  
18 18 #define LINE_ACCOUNT_CERTIFICATE "line-certificate"
19 19 #define LINE_ACCOUNT_AUTH_TOKEN "line-auth-token"
... ...
libpurple/line.thrift
... ... @@ -198,7 +198,7 @@ struct LoginResult {
198 198 }
199 199  
200 200 struct Message {
201   - 1: string from;
  201 + 1: string from_;
202 202 2: string to;
203 203 3: MIDType toType;
204 204 4: string id;
... ... @@ -248,6 +248,13 @@ struct Room {
248 248 10: list<Contact> contacts;
249 249 }
250 250  
  251 +struct RSAKey {
  252 + 1: string keynm;
  253 + 2: string nvalue;
  254 + 3: string evalue;
  255 + 4: string sessionKey;
  256 +}
  257 +
251 258 exception TalkException {
252 259 1: ErrorCode code;
253 260 2: string reason;
... ... @@ -298,6 +305,9 @@ service TalkService {
298 305 Room getRoom(
299 306 2: string roomId) throws(1: TalkException e);
300 307  
  308 + RSAKey getRSAKeyInfo(
  309 + 2: IdentityProvider provider) throws(1: TalkException e);
  310 +
301 311 LoginResult loginWithIdentityCredentialForCertificate(
302 312 8: IdentityProvider identityProvider,
303 313 3: string identifier,
... ...
libpurple/pluginmain.cpp
... ... @@ -40,7 +40,7 @@ static void init_info(PurplePluginInfo &amp;i) {
40 40 i.summary = (char *)"Plugin for Naver LINE";
41 41 i.description = (char *)"Plugin for Naver LINE";
42 42 i.author = (char *)"Matti Virkkunen <mvirkkunen@gmail.com>";
43   - i.homepage = (char *)"https://github.com/mvirkkunen/purple-line";
  43 + i.homepage = (char *)"http://altrepo.eu/git/purple-line";
44 44  
45 45 i.destroy = line_plugin_destroy;
46 46 i.extra_info = (void *)&prpl_info;
... ...
libpurple/poller.cpp
... ... @@ -35,7 +35,8 @@ void Poller::fetch_operations() {
35 35 fetch_operations();
36 36 return;
37 37 } else if (status != 200) {
38   - purple_debug_warning("line", "fetchOperations error %d. TODO: Retry after a timeout", status);
  38 + purple_debug_warning("line", "fetchOperations error %d. TODO: Retry after a timeout.\n",
  39 + status);
39 40 return;
40 41 }
41 42  
... ...
libpurple/purpleline.cpp
... ... @@ -284,7 +284,7 @@ int PurpleLine::send_message(std::string to, const char *markup) {
284 284 line::Message msg;
285 285  
286 286 msg.contentType = line::ContentType::NONE;
287   - msg.from = profile.mid;
  287 + msg.from_ = profile.mid;
288 288 msg.to = to;
289 289 msg.text = markup_unescape(text);
290 290  
... ... @@ -294,8 +294,6 @@ int PurpleLine::send_message(std::string to, const char *markup) {
294 294 }
295 295  
296 296 if (img_found) {
297   - // Image test
298   -
299 297 int image_id = std::stoi((char *)g_datalist_get_data(&attributes, "id"));
300 298 g_datalist_clear(&attributes);
301 299  
... ... @@ -315,7 +313,7 @@ int PurpleLine::send_message(std::string to, const char *markup) {
315 313 line::Message msg;
316 314  
317 315 msg.contentType = line::ContentType::IMAGE;
318   - msg.from = profile.mid;
  316 + msg.from_ = profile.mid;
319 317 msg.to = to;
320 318  
321 319 send_message(msg, [this, img_data](line::Message &msg_back) {
... ... @@ -450,7 +448,7 @@ void PurpleLine::signal_blist_node_removed(PurpleBlistNode *node) {
450 448  
451 449 char *id_ptr = (char *)g_hash_table_lookup(components, "id");
452 450 if (!id_ptr) {
453   - purple_debug_warning("line", "Tried to remove a chat with no id.");
  451 + purple_debug_warning("line", "Tried to remove a chat with no id.\n");
454 452 return;
455 453 }
456 454  
... ... @@ -477,7 +475,7 @@ void PurpleLine::signal_blist_node_removed(PurpleBlistNode *node) {
477 475 }
478 476 });
479 477 } else {
480   - purple_debug_warning("line", "Tried to remove a chat with no type.");
  478 + purple_debug_warning("line", "Tried to remove a chat with no type.\n");
481 479 return;
482 480 }
483 481 }
... ... @@ -502,6 +500,10 @@ void PurpleLine::fetch_conversation_history(PurpleConversation *conv, int count,
502 500 if (end_seq_p)
503 501 end_seq = *end_seq_p;
504 502  
  503 + purple_debug_info("line",
  504 + "Fetching history: end_seq=%" G_GINT64_FORMAT " , count=%d, requested=%d\n",
  505 + end_seq, count, requested);
  506 +
505 507 if (end_seq != -1)
506 508 c_out->send_getPreviousMessages(name, end_seq - 1, count);
507 509 else
... ... @@ -600,6 +602,8 @@ void PurpleLine::fetch_conversation_history(PurpleConversation *conv, int count,
600 602 delete end_seq_p;
601 603  
602 604 purple_conversation_set_data(conv, "line-end-seq", new int64_t(new_end_seq));
  605 +
  606 + purple_debug_info("line", "History done: new_end_seq=%" G_GINT64_FORMAT "\n", new_end_seq);
603 607 });
604 608 }
605 609  
... ...
libpurple/purpleline.hpp
... ... @@ -14,8 +14,6 @@
14 14 #include "poller.hpp"
15 15 #include "pinverifier.hpp"
16 16  
17   -#define LINEPRPL_ID "prpl-mvirkkunen-line"
18   -
19 17 class ThriftClient;
20 18  
21 19 template <typename T>
... ... @@ -163,6 +161,7 @@ private:
163 161 void login_start();
164 162  
165 163 void get_auth_token();
  164 + std::string get_encrypted_credentials(line::RSAKey &key);
166 165 void set_auth_token(std::string auth_token);
167 166 void get_last_op_revision();
168 167 void get_profile();
... ...
libpurple/purpleline_chats.cpp
... ... @@ -177,7 +177,7 @@ GList *PurpleLine::chat_info() {
177 177 void PurpleLine::join_chat(GHashTable *components) {
178 178 char *id_ptr = (char *)g_hash_table_lookup(components, "id");
179 179 if (!id_ptr) {
180   - purple_debug_warning("line", "Tried to join a chat with no id.");
  180 + purple_debug_warning("line", "Tried to join a chat with no id.\n");
181 181 return;
182 182 }
183 183  
... ... @@ -186,7 +186,7 @@ void PurpleLine::join_chat(GHashTable *components) {
186 186 ChatType type = get_chat_type((char *)g_hash_table_lookup(components, "type"));
187 187  
188 188 if (type == ChatType::ANY) {
189   - purple_debug_warning("line", "Tried to join a chat with weird type.");
  189 + purple_debug_warning("line", "Tried to join a chat with weird type.\n");
190 190 return;
191 191 }
192 192  
... ... @@ -210,7 +210,7 @@ void PurpleLine::join_chat(GHashTable *components) {
210 210 c_out->recv_getGroup(group);
211 211  
212 212 if (!group.__isset.id) {
213   - purple_debug_warning("line", "Couldn't get group: %s", group.id.c_str());
  213 + purple_debug_warning("line", "Couldn't get group: %s\n", group.id.c_str());
214 214 return;
215 215 }
216 216  
... ... @@ -246,7 +246,7 @@ void PurpleLine::join_chat_success(ChatType type, std::string id) {
246 246 void PurpleLine::reject_chat(GHashTable *components) {
247 247 char *id_ptr = (char *)g_hash_table_lookup(components, "id");
248 248 if (!id_ptr) {
249   - purple_debug_warning("line", "Tried to reject an invitation with no id.");
  249 + purple_debug_warning("line", "Tried to reject an invitation with no id.\n");
250 250 return;
251 251 }
252 252  
... ... @@ -282,7 +282,7 @@ void PurpleLine::chat_leave(int id) {
282 282 int PurpleLine::chat_send(int id, const char *message, PurpleMessageFlags flags) {
283 283 PurpleConversation *conv = purple_find_chat(conn, id);
284 284 if (!conv) {
285   - purple_debug_warning("line", "Tried to send to a nonexistent chat.");
  285 + purple_debug_warning("line", "Tried to send to a nonexistent chat.\n");
286 286 return 0;
287 287 }
288 288  
... ...
libpurple/purpleline_cmds.cpp
... ... @@ -5,7 +5,7 @@ void PurpleLine::register_commands() {
5 5 "sticker",
6 6 "w",
7 7 PURPLE_CMD_P_PRPL,
8   - (PurpleCmdFlag)(PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT),
  8 + (PurpleCmdFlag)(PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT),
9 9 LINE_PRPL_ID,
10 10 WRAPPER(PurpleLine::cmd_sticker),
11 11 "Sends a sticker. The argument should be of the format VER/PKGID/ID.",
... ... @@ -15,8 +15,8 @@ void PurpleLine::register_commands() {
15 15 "history",
16 16 "w",
17 17 PURPLE_CMD_P_PRPL,
18   - (PurpleCmdFlag)
19   - (PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS),
  18 + (PurpleCmdFlag)(PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT
  19 + | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS),
20 20 LINE_PRPL_ID,
21 21 WRAPPER(PurpleLine::cmd_history),
22 22 "Shows more chat history. Optional argument specifies number of messages to show.",
... ... @@ -26,7 +26,7 @@ void PurpleLine::register_commands() {
26 26 "open",
27 27 "w",
28 28 PURPLE_CMD_P_PRPL,
29   - (PurpleCmdFlag)(PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT),
  29 + (PurpleCmdFlag)(PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT),
30 30 LINE_PRPL_ID,
31 31 WRAPPER(PurpleLine::cmd_open),
32 32 "Opens an attachment (image, audio) by number.",
... ... @@ -61,7 +61,6 @@ PurpleCmdRet PurpleLine::cmd_sticker(PurpleConversation *conv,
61 61 }
62 62  
63 63 msg.contentType = line::ContentType::STICKER;
64   - msg.from = profile.mid;
65 64 msg.to = purple_conversation_get_name(conv);
66 65  
67 66 write_message(msg, false);
... ...
libpurple/purpleline_login.cpp
1 1 #include "purpleline.hpp"
2 2  
  3 +#include <sstream>
  4 +#include <iomanip>
  5 +
3 6 #include <core.h>
4 7  
  8 +#include <gcrypt.h>
  9 +
  10 +static std::string hex_to_bytes(std::string hex) {
  11 + if (hex.size() % 2 != 0)
  12 + hex = std::string("0") + hex;
  13 +
  14 + std::string result(hex.size() / 2, '\0');
  15 + for (size_t i = 0; i < result.size(); i++)
  16 + result[i] = std::stoi(hex.substr(i * 2, 2), nullptr, 16);
  17 +
  18 + return result;
  19 +}
  20 +
  21 +static std::string bytes_to_hex(std::string bytes) {
  22 + std::ostringstream ss;
  23 +
  24 + for (size_t i = 0; i < bytes.size(); i++)
  25 + ss << std::hex << std::setfill('0') << std::setw(2) << (unsigned)(bytes[i] & 0xff);
  26 +
  27 + return ss.str();
  28 +}
  29 +
5 30 void PurpleLine::login_start() {
6 31 purple_connection_set_state(conn, PURPLE_CONNECTING);
7 32 purple_connection_update_progress(conn, "Logging in", 0, 3);
... ... @@ -49,37 +74,21 @@ void PurpleLine::login_start() {
49 74 }
50 75  
51 76 void PurpleLine::get_auth_token() {
52   - std::string certificate(purple_account_get_string(acct, LINE_ACCOUNT_CERTIFICATE, ""));
53   -
54 77 purple_debug_info("line", "Logging in with credentials to get new auth token.\n");
55 78  
56   - std::string ui_name = "purple-line";
57   -
58   - GHashTable *ui_info = purple_core_get_ui_info();
59   - gpointer ui_name_p = g_hash_table_lookup(ui_info, "name");
60   - if (ui_name_p)
61   - ui_name = (char *)ui_name_p;
62   -
63   - c_out->set_url(LINE_TALK_URL LINE_LOGIN_PATH);
64   -
65   - c_out->send_loginWithIdentityCredentialForCertificate(
66   - line::IdentityProvider::LINE,
67   - purple_account_get_username(acct),
68   - purple_account_get_password(acct),
69   - true,
70   - "127.0.0.1",
71   - ui_name,
72   - certificate);
  79 + c_out->send_getRSAKeyInfo(line::IdentityProvider::LINE);
73 80 c_out->send([this]() {
74   - c_out->set_url(LINE_TALK_URL LINE_COMMAND_PATH);
75   -
76   - line::LoginResult result;
  81 + line::RSAKey key;
  82 + std::string credentials;
77 83  
78 84 try {
79   - c_out->recv_loginWithIdentityCredentialForCertificate(result);
80   - } catch (line::TalkException &err) {
81   - std::string msg = "Could not log in. " + err.reason;
  85 + c_out->recv_getRSAKeyInfo(key);
82 86  
  87 + credentials = get_encrypted_credentials(key);
  88 + } catch (std::exception &ex) {
  89 + std::string msg = std::string("Could not log in. ") + ex.what();
  90 +
  91 + conn->wants_to_die = TRUE;
83 92 purple_connection_error(
84 93 conn,
85 94 msg.c_str());
... ... @@ -87,42 +96,165 @@ void PurpleLine::get_auth_token() {
87 96 return;
88 97 }
89 98  
90   - if (result.type == line::LoginResultType::SUCCESS && result.authToken != "")
91   - {
92   - set_auth_token(result.authToken);
  99 + std::string certificate(purple_account_get_string(acct, LINE_ACCOUNT_CERTIFICATE, ""));
93 100  
94   - get_last_op_revision();
95   - }
96   - else if (result.type == line::LoginResultType::REQUIRE_DEVICE_CONFIRM)
97   - {
98   - purple_debug_info("line", "Starting PIN verification.\n");
  101 + std::string ui_name = "purple-line";
99 102  
100   - pin_verifier.verify(result, [this](std::string auth_token, std::string certificate) {
101   - if (certificate != "") {
102   - purple_account_set_string(
103   - acct,
104   - LINE_ACCOUNT_CERTIFICATE,
105   - certificate.c_str());
106   - }
  103 + GHashTable *ui_info = purple_core_get_ui_info();
  104 + gpointer ui_name_p = g_hash_table_lookup(ui_info, "name");
  105 + if (ui_name_p)
  106 + ui_name = (char *)ui_name_p;
107 107  
108   - set_auth_token(auth_token);
  108 + c_out->send_loginWithIdentityCredentialForCertificate(
  109 + line::IdentityProvider::LINE,
  110 + key.keynm,
  111 + credentials,
  112 + true,
  113 + "127.0.0.1",
  114 + ui_name,
  115 + certificate);
  116 + c_out->send([this]() {
  117 + line::LoginResult result;
  118 +
  119 + try {
  120 + c_out->recv_loginWithIdentityCredentialForCertificate(result);
  121 + } catch (line::TalkException &err) {
  122 + std::string msg = "Could not log in. " + err.reason;
  123 +
  124 + conn->wants_to_die = TRUE;
  125 + purple_connection_error(
  126 + conn,
  127 + msg.c_str());
  128 +
  129 + return;
  130 + }
  131 +
  132 + if (result.type == line::LoginResultType::SUCCESS && result.authToken != "")
  133 + {
  134 + set_auth_token(result.authToken);
109 135  
110 136 get_last_op_revision();
111   - });
112   - }
113   - else
114   - {
115   - std::stringstream ss("Could not log in. Bad LoginResult type: ");
116   - ss << result.type;
117   - std::string msg = ss.str();
  137 + }
  138 + else if (result.type == line::LoginResultType::REQUIRE_DEVICE_CONFIRM)
  139 + {
  140 + purple_debug_info("line", "Starting PIN verification.\n");
118 141  
119   - purple_connection_error(
120   - conn,
121   - msg.c_str());
122   - }
  142 + pin_verifier.verify(result, [this](std::string auth_token, std::string certificate) {
  143 + if (certificate != "") {
  144 + purple_account_set_string(
  145 + acct,
  146 + LINE_ACCOUNT_CERTIFICATE,
  147 + certificate.c_str());
  148 + }
  149 +
  150 + set_auth_token(auth_token);
  151 +
  152 + get_last_op_revision();
  153 + });
  154 + }
  155 + else
  156 + {
  157 + std::stringstream ss("Could not log in. Bad LoginResult type: ");
  158 + ss << result.type;
  159 + std::string msg = ss.str();
  160 +
  161 + purple_connection_error(
  162 + conn,
  163 + msg.c_str());
  164 + }
  165 + });
123 166 });
124 167 }
125 168  
  169 +// This may throw.
  170 +std::string PurpleLine::get_encrypted_credentials(line::RSAKey &key) {
  171 + if (!gcry_check_version(GCRYPT_VERSION)) {
  172 + std::string err = "libgcrypt version mismatch (compiled: " GCRYPT_VERSION " runtime: ";
  173 + err += gcry_check_version(nullptr);
  174 + err += ")";
  175 +
  176 + throw std::runtime_error(err);
  177 + }
  178 +
  179 + if (!gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P)) {
  180 + gcry_control(GCRYCTL_DISABLE_SECMEM, 0);
  181 +
  182 + gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
  183 + }
  184 +
  185 + std::string username = purple_account_get_username(acct);
  186 + std::string password = purple_account_get_password(acct);
  187 +
  188 + if (username.size() > 255)
  189 + throw std::runtime_error("Username is too long.");
  190 +
  191 + if (password.size() > 255)
  192 + throw std::runtime_error("Password is too long.");
  193 +
  194 + std::ostringstream buf;
  195 +
  196 + buf << (char)key.sessionKey.size() << key.sessionKey;
  197 + buf << (char)username.size() << username;
  198 + buf << (char)password.size() << password;
  199 +
  200 + std::string modulus = hex_to_bytes(key.nvalue);
  201 + std::string exponent = hex_to_bytes(key.evalue);
  202 +
  203 + std::string credentials = buf.str();
  204 +
  205 + gpg_error_t err;
  206 +
  207 + gcry_sexp_t public_key, plaintext, ciphertext, result;
  208 +
  209 + err = gcry_sexp_build(
  210 + &public_key, nullptr,
  211 + "(public-key (rsa (n %b) (e %b)))",
  212 + (int)modulus.size(), modulus.c_str(),
  213 + (int)exponent.size(), exponent.c_str());
  214 +
  215 + if (err)
  216 + throw std::runtime_error(std::string("ligbcrypt public key error: ") + gpg_strerror(err));
  217 +
  218 + err = gcry_sexp_build(
  219 + &plaintext, nullptr,
  220 + "(data (flags pkcs1) (value %b))",
  221 + (int)credentials.size(), credentials.c_str());
  222 +
  223 + if (err) {
  224 + gcry_sexp_release(public_key);
  225 + throw std::runtime_error(std::string("ligbcrypt data error: ") + gpg_strerror(err));
  226 + }
  227 +
  228 + err = gcry_pk_encrypt(&ciphertext, plaintext, public_key);
  229 +
  230 + gcry_sexp_release(plaintext);
  231 + gcry_sexp_release(public_key);
  232 +
  233 + if (err)
  234 + throw std::runtime_error(std::string("libgcrypt encryption error: ") + gpg_strerror(err));
  235 +
  236 + result = gcry_sexp_find_token(ciphertext, "a", 0);
  237 +
  238 + gcry_sexp_release(ciphertext);
  239 +
  240 + if (!result)
  241 + throw std::runtime_error("libgcrypt result token not found");
  242 +
  243 + size_t result_data_len;
  244 + const char *result_data = gcry_sexp_nth_data(result, 1, &result_data_len);
  245 +
  246 + if (!result_data) {
  247 + gcry_sexp_release(result);
  248 + throw std::runtime_error("libgcrypt result token value not found");
  249 + }
  250 +
  251 + std::string result_bytes(result_data, result_data_len);
  252 +
  253 + gcry_sexp_release(result);
  254 +
  255 + return bytes_to_hex(result_bytes);
  256 +}
  257 +
126 258 void PurpleLine::set_auth_token(std::string auth_token) {
127 259 purple_account_set_string(acct, LINE_ACCOUNT_AUTH_TOKEN, auth_token.c_str());
128 260  
... ...
libpurple/purpleline_write.cpp
... ... @@ -49,7 +49,7 @@ void PurpleLine::write_message(line::Message &amp;msg, bool replay) {
49 49 int flags = 0;
50 50 time_t mtime = (time_t)(msg.createdTime / 1000);
51 51  
52   - bool sent = (msg.from == profile.mid);
  52 + bool sent = (msg.from_ == profile.mid);
53 53  
54 54 if (std::find(recent_messages.cbegin(), recent_messages.cend(), msg.id)
55 55 != recent_messages.cend())
... ... @@ -59,17 +59,17 @@ void PurpleLine::write_message(line::Message &amp;msg, bool replay) {
59 59 }
60 60  
61 61 // Hack
62   - if (msg.from == msg.to)
  62 + if (msg.from_ == msg.to)
63 63 push_recent_message(msg.id);
64 64  
65 65 PurpleConversation *conv = purple_find_conversation_with_account(
66 66 (msg.toType == line::MIDType::USER ? PURPLE_CONV_TYPE_IM : PURPLE_CONV_TYPE_CHAT),
67   - ((!sent && msg.toType == line::MIDType::USER) ? msg.from.c_str() : msg.to.c_str()),
  67 + ((!sent && msg.toType == line::MIDType::USER) ? msg.from_.c_str() : msg.to.c_str()),
68 68 acct);
69 69  
70 70 // If this is a new received IM, create the conversation if it doesn't exist
71 71 if (!conv && !sent && msg.toType == line::MIDType::USER)
72   - conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, acct, msg.from.c_str());
  72 + conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, acct, msg.from_.c_str());
73 73  
74 74 // If this is a new conversation, we're not replaying history and history hasn't been fetched
75 75 // yet, queue the message instead of showing it.
... ... @@ -247,7 +247,7 @@ void PurpleLine::write_message(line::Message &amp;msg, bool replay) {
247 247 if (sent) {
248 248 // Messages sent by user (sync from other devices)
249 249  
250   - write_message(conv, msg.from, text, mtime, flags | PURPLE_MESSAGE_SEND);
  250 + write_message(conv, msg.from_, text, mtime, flags | PURPLE_MESSAGE_SEND);
251 251 } else {
252 252 // Messages received from other users
253 253  
... ... @@ -256,12 +256,12 @@ void PurpleLine::write_message(line::Message &amp;msg, bool replay) {
256 256 if (replay) {
257 257 // Write replayed messages instead of serv_got_* to avoid Pidgin's IM sound
258 258  
259   - write_message(conv, msg.from, text, mtime, flags);
  259 + write_message(conv, msg.from_, text, mtime, flags);
260 260 } else {
261 261 if (msg.toType == line::MIDType::USER) {
262 262 serv_got_im(
263 263 conn,
264   - msg.from.c_str(),
  264 + msg.from_.c_str(),
265 265 text.c_str(),
266 266 (PurpleMessageFlags)flags,
267 267 mtime);
... ... @@ -269,7 +269,7 @@ void PurpleLine::write_message(line::Message &amp;msg, bool replay) {
269 269 serv_got_chat_in(
270 270 conn,
271 271 purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)),
272   - msg.from.c_str(),
  272 + msg.from_.c_str(),
273 273 (PurpleMessageFlags)flags,
274 274 text.c_str(),
275 275 mtime);
... ...