diff --git a/README.md b/README.md index 5617ac4..fda50fe 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ This is a re-write of [purple-gowhatsapp](https://github.com/hoehermann/purple-g Standard features: -* Connecting to existing account via QR-code. +* Connecting to existing account via QR code or 8-character code. * Receiving messages, sending messages. * Receiving files (image, video and note, audio and voice, document, sticker). * Received images are displayed in the conversation window (optional). @@ -154,8 +154,8 @@ For sending opus in ogg audio files as voice messages, add a static win32 build You must enter your phone's internationalized number followed by `@s.whatsapp.net`. Example: `123456789` from Germany would use `49123456789@s.whatsapp.net`. -* Upon login, a QR code is shown in a Pidgin request window. - Using your phone's camera, scan the code within 20 seconds – just like you would do with WhatsApp Web. +* Upon login, a QR code and the 8-character code is shown in a Pidgin request window. + Using your phone's camera, scan the code within 20 seconds or enter the 8-character code on your main device – just like you would do with WhatsApp Web. *Note:* On headless clients such as Spectrum, the QR code will be wrapped in a message by a fake contact called "Logon QR Code". You may need to temporarily configure your UI to accept messages from unsolicited users for linking purposes. Wait until the connection has been fully set up. Unfortunately, there is no progress indicator while keys are exchanged and old messages are fetched. Usually, a couple of seconds is enough. Some power users with many groups and contacts reported the process can take more than a minute. If the plug-in is not yet ready, outgoing messages may be dropped silently (see issue #142). diff --git a/src/c/bridge.h b/src/c/bridge.h index e63b5ee..b2bd98a 100644 --- a/src/c/bridge.h +++ b/src/c/bridge.h @@ -71,6 +71,9 @@ struct gowhatsapp_message { char *remoteJid; /// conversation identifier (may be a single contact or a group) char *senderJid; /// message author's identifier (useful in group chats) char *text; /// the message payload (interpretation depends on type) + char *pairing_code; /// 6-character pairing code + char *pairing_qrdata; /// the pairing QR-code raw data + char *pairing_qrterminal; /// graphical QR for printing on a terminal char *name; /// remote user's name (chosen by them) or filename (in case of attachment) void *blob; /// binary payload (used for inlining images) char **participants; /// list of participants (for group chats) diff --git a/src/c/display_message.c b/src/c/display_message.c index 088c535..d576af8 100644 --- a/src/c/display_message.c +++ b/src/c/display_message.c @@ -4,7 +4,6 @@ void gowhatsapp_display_text_message(PurpleConnection *pc, gowhatsapp_message_t *gwamsg, PurpleMessageFlags flags) { g_return_if_fail(pc != NULL); // WhatsApp is a plain-text protocol, but Pidgin expects HTML - // NOTE: This turns newlines into br-tags which may mess up textual representation of QR-codes gchar * text = purple_markup_escape_text(gwamsg->text, -1); gowhatsapp_display_message_common(pc, gwamsg->senderJid, gwamsg->remoteJid, text, gwamsg->timestamp, gwamsg->isGroup, gwamsg->isOutgoing, gwamsg->name, flags); g_free(text); diff --git a/src/c/login.c b/src/c/login.c index ccebb03..74434a3 100644 --- a/src/c/login.c +++ b/src/c/login.c @@ -83,7 +83,7 @@ gowhatsapp_store_credentials(PurpleAccount *account, char *credentials) // Pidgin stores the credentials in the account settings // since commit ee89203, spectrum supports this out of the box // in bitlbee, this has no effect - // TODO: ask spectrum maintainer if storing in password woukd okay, too + // TODO: ask spectrum maintainer if storing in password would okay, too // or do not store credentials at all (just use the username for look-up) purple_account_set_string(account, GOWHATSAPP_CREDENTIALS_KEY, credentials); diff --git a/src/c/qrcode.c b/src/c/qrcode.c index b573642..6b67c02 100644 --- a/src/c/qrcode.c +++ b/src/c/qrcode.c @@ -18,7 +18,7 @@ gowhatsapp_close_qrcode(PurpleAccount *account) } static void -gowhatsapp_display_qrcode(PurpleAccount *account, const char * challenge, void * image_data, size_t image_data_len) +gowhatsapp_display_qrcode(PurpleAccount *account, const char *pairing_code, const char *qr_data, void * image_data, size_t image_data_len) { g_return_if_fail(account != NULL); @@ -26,19 +26,27 @@ gowhatsapp_display_qrcode(PurpleAccount *account, const char * challenge, void * PurpleRequestFieldGroup *group = purple_request_field_group_new(NULL); purple_request_fields_add_group(fields, group); - PurpleRequestField *string_field = purple_request_field_string_new("qr_string", "QR Code Data", challenge, FALSE); - purple_request_field_group_add_field(group, string_field); - PurpleRequestField *image_field = purple_request_field_image_new("qr_image", "QR Code Image", image_data, image_data_len); - purple_request_field_group_add_field(group, image_field); + { + PurpleRequestField *string_code = purple_request_field_string_new("pairing_code", "Pairing Code", pairing_code, FALSE); + purple_request_field_group_add_field(group, string_code); + } + { + PurpleRequestField *string_field = purple_request_field_string_new("qr_data", "QR Code Data", qr_data, FALSE); + purple_request_field_group_add_field(group, string_field); + } + { + PurpleRequestField *image_field = purple_request_field_image_new("qr_image", "QR Code Image", image_data, image_data_len); + purple_request_field_group_add_field(group, image_field); + } const char *username = purple_account_get_username(account); - char *secondary = g_strdup_printf("WhatsApp account %s (multi-device mode must be enabled)", username); // MEMCHECK: released here + char *secondary = g_strdup_printf("WhatsApp account %s", username); // MEMCHECK: released here gowhatsapp_close_qrcode(account); purple_request_fields( account, /*handle*/ "Logon QR Code", /*title*/ - "Please scan this QR code with your phone", /*primary*/ + "Please enter pairing code or scan the QR code", /*primary*/ secondary, /*secondary*/ fields, /*fields*/ "OK", G_CALLBACK(null_cb), /*OK*/ @@ -59,33 +67,37 @@ gowhatsapp_handle_qrcode(PurpleConnection *pc, gowhatsapp_message_t *gwamsg) if (!ui_ops || !ui_ops->request_fields || gwamsg->blobsize <= 0) { // The UI hasn't implemented the func we want, just output as a message instead PurpleMessageFlags flags = PURPLE_MESSAGE_RECV; - gchar *msg_out; int img_id = 0; if (gwamsg->blobsize > 0) { img_id = purple_imgstore_add_with_id(gwamsg->blob, gwamsg->blobsize, NULL); // MEMCHECK: released including gwamsg->blob by purple_imgstore_unref_by_id (see below) } + gchar *msg_img = NULL; if (img_id > 0) { gwamsg->blob = NULL; // MEMCHECK: not our memory to free any more - msg_out = g_strdup_printf( // MEMCHECK: msg_out released here (see below) - "%s
\"%s\"/
%s", - "Please scan this QR code with your phone and WhatsApp multi-device mode enabled:", img_id, gwamsg->text, gwamsg->name - ); + msg_img = g_strdup_printf("", img_id); // MEMCHECK: released here (see below) flags |= PURPLE_MESSAGE_IMAGES; } else { - msg_out = g_strdup_printf( // MEMCHECK: msg_out released here (see below) - "%s
%s
%s", - "Please scan this QR code with your phone and WhatsApp multi-device mode enabled:", gwamsg->text, gwamsg->name - ); + // NOTE: This turns the newlines into br-tags. Front-ends should know what they are doing. + gchar * qrterminal_html = purple_markup_escape_text(gwamsg->pairing_qrterminal, -1); // MEMCHECK: released here (see below) + msg_img = g_strdup_printf("Your UI does not handle images. The next lines emulate the QR code with text characters. If viewed with a mono-spaced font, scanning may succeed. In case you see the raw HTML (with br-tags), you need to convert them into newlines first.
%s", qrterminal_html); // MEMCHECK: released here (see below) + g_free(qrterminal_html); } + gchar *msg_out = g_strdup_printf( + "Please enter pairing code %s or scan the QR code with your phone.
%s
In case the QR code above does not work, this is the challenge data. Use the QR code generator of your choice to turn it into an image:
%s", + gwamsg->pairing_code, + msg_img, + gwamsg->pairing_qrdata + ); // MEMCHECK: released here (see below) + g_free(msg_img); const gchar *who = "Logon QR Code"; purple_serv_got_im(pc, who, msg_out, flags, time(NULL)); + g_free(msg_out); if (img_id > 0) { purple_imgstore_unref_by_id(img_id); } - g_free(msg_out); } else { PurpleAccount *account = purple_connection_get_account(pc); - gowhatsapp_display_qrcode(account, gwamsg->text, gwamsg->blob, gwamsg->blobsize); + gowhatsapp_display_qrcode(account, gwamsg->pairing_code, gwamsg->pairing_qrdata, gwamsg->blob, gwamsg->blobsize); } g_free(gwamsg->blob); } diff --git a/src/go/bridge.go b/src/go/bridge.go index 4d1c56a..aa05389 100644 --- a/src/go/bridge.go +++ b/src/go/bridge.go @@ -259,16 +259,18 @@ func gowhatsapp_go_request_profile_picture(account *PurpleAccount, who *C.char, } /* - * This will display a QR code via PurpleRequest API. + * This will display a QR code via PurpleRequest API + * or in a conversation window (depending on UI features and user settings). */ -func purple_display_qrcode(account *PurpleAccount, terminal string, challenge string, png []byte) { +func purple_display_qrcode(account *PurpleAccount, piring_code string, qr_data string, qr_terminal string, png []byte) { cmessage := C.struct_gowhatsapp_message{ - account: account, - msgtype: C.char(C.gowhatsapp_message_type_login), - text: C.CString(challenge), - name: C.CString(terminal), - blob: C.CBytes(png), - blobsize: C.size_t(len(png)), + account: account, + msgtype: C.char(C.gowhatsapp_message_type_login), + pairing_code: C.CString(piring_code), + pairing_qrdata: C.CString(qr_data), + pairing_qrterminal: C.CString(qr_terminal), + blob: C.CBytes(png), + blobsize: C.size_t(len(png)), } C.gowhatsapp_process_message_bridge(cmessage) } diff --git a/src/go/login.go b/src/go/login.go index 85512e9..c9d69a9 100644 --- a/src/go/login.go +++ b/src/go/login.go @@ -193,14 +193,13 @@ func (handler *Handler) generate_pairing_code() (string, error) { * send a list of codes which can be turned into QR codes for scanning with the offical app. */ func (handler *Handler) handle_qrcode(qrcodes []string) { - var stringBuilder strings.Builder pairing_code, err := handler.generate_pairing_code() if err != nil { purple_error(handler.account, fmt.Sprintf("%#v", err), ERROR_FATAL) } qrcode_data := qrcodes[0] // use only first code for now // TODO: emit events to destroy and update the code in the ui - fmt.Fprintf(&stringBuilder, "Enter pairing code %s or scan this code to log in:\n%s\n", pairing_code, qrcode_data) + var stringBuilder strings.Builder qrterminal.GenerateHalfBlock(qrcode_data, qrterminal.L, &stringBuilder) size := purple_get_int(handler.account, C.GOWHATSAPP_QRCODE_SIZE_OPTION, 256) var png []byte @@ -210,7 +209,7 @@ func (handler *Handler) handle_qrcode(qrcodes []string) { purple_error(handler.account, fmt.Sprintf("%#v", err), ERROR_FATAL) } } - purple_display_qrcode(handler.account, stringBuilder.String(), qrcode_data, png) + purple_display_qrcode(handler.account, pairing_code, qrcode_data, stringBuilder.String(), png) } /*