Add vCard support

Only nicknames, photos, birthdays, addresses, telephone numbers, emails,
JIDs, titles, roles, notes, and URLs are supported

Due to the synopsis array not having enough space, `/vcard photo
open-self` and `/vcard photo save-self` are not documented properly in
the synopsis section of the `/vcard` command, but they are documented in
the arguments section

Fixed memory leak in vcard autocomplete (thanks to debXwoody)
This commit is contained in:
Marouane L
2022-09-06 17:29:07 +01:00
parent fc8455ba34
commit f934c5b59f
25 changed files with 3688 additions and 7 deletions

View File

@@ -1517,6 +1517,10 @@ cons_time_setting(void)
char* pref_time_lastactivity = prefs_get_string(PREF_TIME_LASTACTIVITY);
cons_show("Time last activity (/time) : %s", pref_time_lastactivity);
g_free(pref_time_lastactivity);
char* pref_time_vcard = prefs_get_string(PREF_TIME_VCARD);
cons_show("Time vCard (/time) : %s", pref_time_vcard);
g_free(pref_time_vcard);
}
void
@@ -2262,6 +2266,10 @@ cons_executable_setting(void)
gchar* editor = prefs_get_string(PREF_COMPOSE_EDITOR);
cons_show("Default '/editor' command (/executable editor) : %s", editor);
g_free(editor);
gchar* vcard_cmd = prefs_get_string(PREF_VCARD_PHOTO_CMD);
cons_show("Default '/vcard photo open' command (/executable vcard_photo) : %s", vcard_cmd);
g_free(vcard_cmd);
}
void

View File

@@ -230,6 +230,11 @@ char* confwin_get_string(ProfConfWin* confwin);
void xmlwin_show(ProfXMLWin* xmlwin, const char* const msg);
char* xmlwin_get_string(ProfXMLWin* xmlwin);
// vCard window
void vcardwin_show_vcard_config(ProfVcardWin* vcardwin);
char* vcardwin_get_string(ProfVcardWin* vcardwin);
void vcardwin_update(void);
// Input window
char* inp_readline(void);
void inp_nonblocking(gboolean reset);
@@ -371,6 +376,7 @@ ProfWin* win_create_muc(const char* const roomjid);
ProfWin* win_create_config(const char* const title, DataForm* form, ProfConfWinCallback submit, ProfConfWinCallback cancel, const void* userdata);
ProfWin* win_create_private(const char* const fulljid);
ProfWin* win_create_plugin(const char* const plugin_name, const char* const tag);
ProfWin* win_create_vcard(vCard* vcard);
void win_update_virtual(ProfWin* window);
void win_free(ProfWin* window);
gboolean win_notify_remind(ProfWin* window);
@@ -396,6 +402,8 @@ void win_show_occupant(ProfWin* window, Occupant* occupant);
void win_show_occupant_info(ProfWin* window, const char* const room, Occupant* occupant);
void win_show_contact(ProfWin* window, PContact contact);
void win_show_info(ProfWin* window, PContact contact);
void win_show_vcard(ProfWin* window, vCard* vcard);
void win_clear(ProfWin* window);
char* win_get_tab_identifier(ProfWin* window);
char* win_to_string(ProfWin* window);

75
src/ui/vcardwin.c Normal file
View File

@@ -0,0 +1,75 @@
/*
* vcardwin.c
* vim: expandtab:ts=4:sts=4:sw=4
*
* Copyright (C) 2022 Marouane L. <techmetx11@disroot.org>
*
* This file is part of Profanity.
*
* Profanity 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.
*
* Profanity 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 Profanity. If not, see <https://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give permission to
* link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two.
*
* You must obey the GNU General Public License in all respects for all of the
* code used other than OpenSSL. If you modify file(s) with this exception, you
* may extend this exception to your version of the file(s), but you are not
* obligated to do so. If you do not wish to do so, delete this exception
* statement from your version. If you delete this exception statement from all
* source files in the program, then also delete it here.
*
*/
#include "ui/ui.h"
#include "ui/window_list.h"
#include "xmpp/vcard.h"
void
vcardwin_show_vcard_config(ProfVcardWin* vcardwin)
{
ProfWin* window = &vcardwin->window;
win_clear(window);
win_show_vcard(window, vcardwin->vcard);
win_println(window, THEME_DEFAULT, "-", "Use '/vcard save' to save changes.");
win_println(window, THEME_DEFAULT, "-", "Use '/help vcard' for more information.");
}
char*
vcardwin_get_string(ProfVcardWin* vcardwin)
{
GString* string = g_string_new("vCard: ");
char* jid = connection_get_barejid();
g_string_append(string, jid);
if (vcardwin->vcard && vcardwin->vcard->modified) {
g_string_append(string, " (modified)");
}
free(jid);
return g_string_free(string, FALSE);
}
void
vcardwin_update(void)
{
ProfVcardWin* win = wins_get_vcard();
if (win) {
vcardwin_show_vcard_config(win);
}
}

View File

@@ -52,6 +52,7 @@
#include "tools/autocomplete.h"
#include "ui/buffer.h"
#include "xmpp/chat_state.h"
#include "xmpp/vcard.h"
#define LAYOUT_SPLIT_MEMCHECK 12345671
#define PROFCHATWIN_MEMCHECK 22374522
@@ -60,6 +61,7 @@
#define PROFCONFWIN_MEMCHECK 64334685
#define PROFXMLWIN_MEMCHECK 87333463
#define PROFPLUGINWIN_MEMCHECK 43434777
#define PROFVCARDWIN_MEMCHECK 68947523
typedef enum {
FIELD_HIDDEN,
@@ -140,7 +142,8 @@ typedef enum {
WIN_CONFIG,
WIN_PRIVATE,
WIN_XML,
WIN_PLUGIN
WIN_PLUGIN,
WIN_VCARD
} win_type_t;
typedef struct prof_win_t
@@ -239,4 +242,11 @@ typedef struct prof_plugin_win_t
unsigned long memcheck;
} ProfPluginWin;
typedef struct prof_vcard_win_t
{
ProfWin window;
vCard* vcard;
unsigned long memcheck;
} ProfVcardWin;
#endif

View File

@@ -281,6 +281,19 @@ win_create_plugin(const char* const plugin_name, const char* const tag)
return &new_win->window;
}
ProfWin*
win_create_vcard(vCard* vcard)
{
ProfVcardWin* new_win = malloc(sizeof(ProfVcardWin));
new_win->window.type = WIN_VCARD;
new_win->window.layout = _win_create_simple_layout();
new_win->vcard = vcard;
new_win->memcheck = PROFVCARDWIN_MEMCHECK;
return &new_win->window;
}
char*
win_get_title(ProfWin* window)
{
@@ -351,7 +364,22 @@ win_get_title(ProfWin* window)
assert(pluginwin->memcheck == PROFPLUGINWIN_MEMCHECK);
return strdup(pluginwin->tag);
}
if (window->type == WIN_VCARD) {
ProfVcardWin* vcardwin = (ProfVcardWin*)window;
assert(vcardwin->memcheck == PROFVCARDWIN_MEMCHECK);
GString* title = g_string_new("vCard ");
char* jid = connection_get_barejid();
g_string_append(title, jid);
if (vcardwin->vcard->modified) {
g_string_append(title, " *");
}
free(jid);
return g_string_free(title, FALSE);
}
return NULL;
}
@@ -474,6 +502,11 @@ win_to_string(ProfWin* window)
g_string_free(gstring, FALSE);
return res;
}
case WIN_VCARD:
{
ProfVcardWin* vcardwin = (ProfVcardWin*)window;
return vcardwin_get_string(vcardwin);
}
default:
return NULL;
}
@@ -1082,6 +1115,162 @@ win_show_info(ProfWin* window, PContact contact)
g_list_free(ordered_resources);
}
void
win_show_vcard(ProfWin* window, vCard* vcard)
{
GList* pointer;
int index = 0;
if (vcard->fullname) {
win_println(window, THEME_DEFAULT, "!", "Full name: %s", vcard->fullname);
}
if (vcard->name.family || vcard->name.given || vcard->name.middle || vcard->name.prefix || vcard->name.suffix) {
win_println(window, THEME_DEFAULT, "!", "Name: ");
if (vcard->name.family) {
win_println(window, THEME_DEFAULT, "!", " Family: %s", vcard->name.family);
}
if (vcard->name.given) {
win_println(window, THEME_DEFAULT, "!", " Given: %s", vcard->name.given);
}
if (vcard->name.middle) {
win_println(window, THEME_DEFAULT, "!", " Middle: %s", vcard->name.middle);
}
if (vcard->name.prefix) {
win_println(window, THEME_DEFAULT, "!", " Prefix: %s", vcard->name.prefix);
}
if (vcard->name.suffix) {
win_println(window, THEME_DEFAULT, "!", " Suffix: %s", vcard->name.suffix);
}
}
index = 0;
for (pointer = g_queue_peek_head_link(vcard->elements); pointer != NULL; pointer = pointer->next, index++) {
assert(pointer->data != NULL);
vcard_element_t* element = (vcard_element_t*)pointer->data;
switch (element->type) {
case VCARD_NICKNAME:
{
win_println(window, THEME_DEFAULT, "!", "[%d] Nickname: %s", index, element->nickname);
break;
}
case VCARD_PHOTO:
{
if (element->photo.external) {
win_println(window, THEME_DEFAULT, "!", "[%d] Photo, External value: %s", index, element->photo.extval);
} else {
win_println(window, THEME_DEFAULT, "!", "[%d] Photo (%s), size: %zu", index, element->photo.type, element->photo.length);
}
break;
}
case VCARD_BIRTHDAY:
{
char* date_format = prefs_get_string(PREF_TIME_VCARD);
gchar* date = g_date_time_format(element->birthday, date_format);
g_free(date_format);
assert(date != NULL);
win_println(window, THEME_DEFAULT, "!", "[%d] Birthday: %s", index, date);
g_free(date);
break;
}
case VCARD_ADDRESS:
{
// Print the header with flags
win_println(window, THEME_DEFAULT, "!", "[%d] Address%s%s%s%s%s%s%s", index,
(element->address.options & VCARD_HOME) ? " [home]" : "",
(element->address.options & VCARD_WORK) == VCARD_WORK ? " [work]" : "",
(element->address.options & VCARD_POSTAL) == VCARD_POSTAL ? " [postal]" : "",
(element->address.options & VCARD_PARCEL) == VCARD_PARCEL ? " [parcel]" : "",
(element->address.options & VCARD_INTL) == VCARD_INTL ? " [international]" : "",
(element->address.options & VCARD_DOM) == VCARD_DOM ? " [domestic]" : "",
(element->address.options & VCARD_PREF) == VCARD_PREF ? " [preferred]" : "");
if (element->address.pobox) {
win_println(window, THEME_DEFAULT, "!", " P.O. Box: %s", element->address.pobox);
}
if (element->address.extaddr) {
win_println(window, THEME_DEFAULT, "!", " Extended address: %s", element->address.extaddr);
}
if (element->address.street) {
win_println(window, THEME_DEFAULT, "!", " Street: %s", element->address.street);
}
if (element->address.locality) {
win_println(window, THEME_DEFAULT, "!", " Locality: %s", element->address.locality);
}
if (element->address.region) {
win_println(window, THEME_DEFAULT, "!", " Region: %s", element->address.region);
}
if (element->address.pcode) {
win_println(window, THEME_DEFAULT, "!", " Postal code: %s", element->address.pcode);
}
if (element->address.country) {
win_println(window, THEME_DEFAULT, "!", " Country: %s", element->address.country);
}
break;
}
case VCARD_TELEPHONE:
{
// Print the header with flags
win_println(window, THEME_DEFAULT, "!", "[%d] Telephone%s%s%s%s%s%s%s%s%s%s%s%s%s", index,
(element->telephone.options & VCARD_HOME) ? " [home]" : "",
(element->telephone.options & VCARD_WORK) == VCARD_WORK ? " [work]" : "",
(element->telephone.options & VCARD_TEL_VOICE) == VCARD_TEL_VOICE ? " [voice]" : "",
(element->telephone.options & VCARD_TEL_FAX) == VCARD_TEL_FAX ? " [fax]" : "",
(element->telephone.options & VCARD_TEL_PAGER) == VCARD_TEL_PAGER ? " [pager]" : "",
(element->telephone.options & VCARD_TEL_MSG) == VCARD_TEL_MSG ? " [msg]" : "",
(element->telephone.options & VCARD_TEL_CELL) == VCARD_TEL_CELL ? " [cell]" : "",
(element->telephone.options & VCARD_TEL_VIDEO) == VCARD_TEL_VIDEO ? " [video]" : "",
(element->telephone.options & VCARD_TEL_BBS) == VCARD_TEL_BBS ? " [bbs]" : "",
(element->telephone.options & VCARD_TEL_MODEM) == VCARD_TEL_MODEM ? " [modem]" : "",
(element->telephone.options & VCARD_TEL_ISDN) == VCARD_TEL_ISDN ? " [isdn]" : "",
(element->telephone.options & VCARD_TEL_PCS) == VCARD_TEL_PCS ? " [pcs]" : "",
(element->telephone.options & VCARD_PREF) == VCARD_PREF ? " [preferred]" : "");
if (element->telephone.number) {
win_println(window, THEME_DEFAULT, "!", " Number: %s", element->telephone.number);
}
break;
}
case VCARD_EMAIL:
{
// Print the header with flags
win_println(window, THEME_DEFAULT, "!", "[%d] E-mail%s%s%s%s%s", index,
(element->email.options & VCARD_HOME) ? " [home]" : "",
(element->email.options & VCARD_WORK) == VCARD_WORK ? " [work]" : "",
(element->email.options & VCARD_EMAIL_X400) == VCARD_EMAIL_X400 ? " [x400]" : "",
(element->email.options & VCARD_EMAIL_INTERNET) == VCARD_EMAIL_INTERNET ? " [internet]" : "",
(element->email.options & VCARD_PREF) == VCARD_PREF ? " [preferred]" : "");
if (element->email.userid) {
win_println(window, THEME_DEFAULT, "!", " ID: %s", element->email.userid);
}
break;
}
case VCARD_JID:
{
win_println(window, THEME_DEFAULT, "!", "[%d] Jabber ID: %s", index, element->jid);
break;
}
case VCARD_TITLE:
{
win_println(window, THEME_DEFAULT, "!", "[%d] Title: %s", index, element->title);
break;
}
case VCARD_ROLE:
{
win_println(window, THEME_DEFAULT, "!", "[%d] Role: %s", index, element->role);
break;
}
case VCARD_NOTE:
{
win_println(window, THEME_DEFAULT, "!", "[%d] Note: %s", index, element->note);
break;
}
case VCARD_URL:
win_println(window, THEME_DEFAULT, "!", "[%d] URL: %s", index, element->url);
break;
}
}
}
void
win_show_status_string(ProfWin* window, const char* const from,
const char* const show, const char* const status,

View File

@@ -711,6 +711,18 @@ wins_new_plugin(const char* const plugin_name, const char* const tag)
return newwin;
}
ProfWin*
wins_new_vcard(vCard* vcard)
{
GList* keys = g_hash_table_get_keys(windows);
int result = _wins_get_next_available_num(keys);
g_list_free(keys);
ProfWin* newwin = win_create_vcard(vcard);
g_hash_table_insert(windows, GINT_TO_POINTER(result), newwin);
return newwin;
}
gboolean
wins_do_notify_remind(void)
{
@@ -805,6 +817,27 @@ wins_get_xmlconsole(void)
return NULL;
}
ProfVcardWin*
wins_get_vcard(void)
{
GList* values = g_hash_table_get_values(windows);
GList* curr = values;
while (curr) {
ProfWin* window = curr->data;
if (window->type == WIN_VCARD) {
ProfVcardWin* vcardwin = (ProfVcardWin*)window;
assert(vcardwin->memcheck == PROFVCARDWIN_MEMCHECK);
g_list_free(values);
return vcardwin;
}
curr = g_list_next(curr);
}
g_list_free(values);
return NULL;
}
GSList*
wins_get_chat_recipients(void)
{

View File

@@ -46,6 +46,7 @@ ProfWin* wins_new_muc(const char* const roomjid);
ProfWin* wins_new_config(const char* const roomjid, DataForm* form, ProfConfWinCallback submit, ProfConfWinCallback cancel, const void* userdata);
ProfWin* wins_new_private(const char* const fulljid);
ProfWin* wins_new_plugin(const char* const plugin_name, const char* const tag);
ProfWin* wins_new_vcard(vCard* vcard);
gboolean wins_chat_exists(const char* const barejid);
GList* wins_get_private_chats(const char* const roomjid);
@@ -61,6 +62,7 @@ ProfConfWin* wins_get_conf(const char* const roomjid);
ProfPrivateWin* wins_get_private(const char* const fulljid);
ProfPluginWin* wins_get_plugin(const char* const tag);
ProfXMLWin* wins_get_xmlconsole(void);
ProfVcardWin* wins_get_vcard(void);
void wins_close_plugin(char* tag);