summaryrefslogtreecommitdiff
path: root/protocols/Gadu-Gadu/src/import.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'protocols/Gadu-Gadu/src/import.cpp')
-rw-r--r--protocols/Gadu-Gadu/src/import.cpp651
1 files changed, 651 insertions, 0 deletions
diff --git a/protocols/Gadu-Gadu/src/import.cpp b/protocols/Gadu-Gadu/src/import.cpp
new file mode 100644
index 0000000000..e4ff066d11
--- /dev/null
+++ b/protocols/Gadu-Gadu/src/import.cpp
@@ -0,0 +1,651 @@
+////////////////////////////////////////////////////////////////////////////////
+// Gadu-Gadu Plugin for Miranda IM
+//
+// Copyright (c) 2003-2006 Adam Strzelecki <ono+miranda@java.pl>
+//
+// 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, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+////////////////////////////////////////////////////////////////////////////////
+
+#include "gg.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// Checks if a group already exists in Miranda with
+// the specified name.
+// Returns 1 if a group with the name exists, returns 0 otherwise.
+int GroupNameExists(const char *name)
+{
+ char idstr[33];
+ DBVARIANT dbv;
+ int i;
+
+ for (i = 0; ; i++) {
+ _itoa(i, idstr, 10);
+ if (db_get_s(NULL, "CListGroups", idstr, &dbv, DBVT_ASCIIZ)) break;
+ if (!strcmp(dbv.pszVal + 1, name)) {
+ DBFreeVariant(&dbv);
+ return 1;
+ }
+ DBFreeVariant(&dbv);
+ }
+ return 0;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Creates a group with a specified name in the
+// Miranda contact list.
+// Returns proper group name
+
+char *CreateGroup(char *groupName)
+{
+ int groupId;
+ char groupIdStr[11];
+ char groupName2[127];
+ char *p;
+ DBVARIANT dbv;
+
+ // Cleanup group name from weird characters
+
+ // Skip first break
+ while(*groupName && *groupName == '\\') groupName++;
+
+ p = strrchr(groupName, '\\');
+ // Cleanup end
+ while(p && !(*(p + 1)))
+ {
+ *p = 0;
+ p = strrchr(groupName, '\\');
+ }
+ // Create upper groups
+ if (p)
+ {
+ *p = 0;
+ CreateGroup(groupName);
+ *p = '\\';
+ }
+
+ // Is this a duplicate?
+ if (!GroupNameExists(groupName))
+ {
+ lstrcpynA(groupName2 + 1, groupName, (int)strlen(groupName) + 1);
+
+ // Find an unused id
+ for (groupId = 0; ; groupId++) {
+ _itoa(groupId, groupIdStr,10);
+ if (db_get_s(NULL, "CListGroups", groupIdStr, &dbv, DBVT_ASCIIZ))
+ break;
+ DBFreeVariant(&dbv);
+ }
+
+ groupName2[0] = 1|GROUPF_EXPANDED; // 1 is required so we never get '\0'
+ db_set_s(NULL, "CListGroups", groupIdStr, groupName2);
+ }
+ return groupName;
+}
+
+char *gg_makecontacts(GGPROTO *gg, int cr)
+{
+ string_t s = string_init(NULL);
+ char *contacts;
+
+ // Readup contacts
+ HANDLE hContact = db_find_first();
+ while (hContact)
+ {
+ char *szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+ if (szProto != NULL && !strcmp(szProto, gg->m_szModuleName) && !db_get_b(hContact, gg->m_szModuleName, "ChatRoom", 0))
+ {
+ DBVARIANT dbv;
+
+ // Readup FirstName
+ if (!db_get_s(hContact, gg->m_szModuleName, "FirstName", &dbv, DBVT_ASCIIZ))
+ {
+ string_append(s, dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ string_append_c(s, ';');
+ // Readup LastName
+ if (!db_get_s(hContact, gg->m_szModuleName, "LastName", &dbv, DBVT_ASCIIZ))
+ {
+ string_append(s, dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ string_append_c(s, ';');
+
+ // Readup Nick
+ if (!db_get_s(hContact, "CList", "MyHandle", &dbv, DBVT_ASCIIZ) || !db_get_s(hContact, gg->m_szModuleName, GG_KEY_NICK, &dbv, DBVT_ASCIIZ))
+ {
+ DBVARIANT dbv2;
+ if (!db_get_s(hContact, gg->m_szModuleName, "NickName", &dbv2, DBVT_ASCIIZ))
+ {
+ string_append(s, dbv2.pszVal);
+ DBFreeVariant(&dbv2);
+ }
+ else
+ string_append(s, dbv.pszVal);
+ string_append_c(s, ';');
+ string_append(s, dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ else
+ string_append_c(s, ';');
+ string_append_c(s, ';');
+
+ // Readup Phone (fixed: uses stored editable phones)
+ if (!db_get_s(hContact, "UserInfo", "MyPhone0", &dbv, DBVT_ASCIIZ))
+ {
+ // Remove SMS postfix
+ char *sms = strstr(dbv.pszVal, " SMS");
+ if (sms) *sms = 0;
+
+ string_append(s, dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ string_append_c(s, ';');
+ // Readup Group
+ if (!db_get_s(hContact, "CList", "Group", &dbv, DBVT_ASCIIZ))
+ {
+ string_append(s, dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ string_append_c(s, ';');
+ // Readup Uin
+ string_append(s, ditoa(db_get_dw(hContact, gg->m_szModuleName, GG_KEY_UIN, 0)));
+ string_append_c(s, ';');
+ // Readup Mail (fixed: uses stored editable mails)
+ if (!db_get_s(hContact, "UserInfo", "Mye-mail0", &dbv, DBVT_ASCIIZ))
+ {
+ string_append(s, dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ if (cr)
+ string_append(s, ";0;;0;\r\n");
+ else
+ string_append(s, ";0;;0;\n");
+ }
+ hContact = db_find_next(hContact);
+ }
+
+ contacts = string_free(s, 0);
+
+#ifdef DEBUGMODE
+ gg->netlog("gg_makecontacts(): \n%s", contacts);
+#endif
+
+ return contacts;
+}
+
+char *strndup(char *str, int c)
+{
+ char *ret = (char*)malloc(c + 1);
+ ret[c] = 0;
+ strncpy(ret, str, c);
+ return ret;
+}
+
+void GGPROTO::parsecontacts(char *contacts)
+{
+ char *p = strchr(contacts, ':'), *n;
+ char *strFirstName, *strLastName, *strNickname, *strNick, *strPhone, *strGroup, *strUin, *strMail;
+ uin_t uin;
+
+ // Skip to proper data
+ if (p && p < strchr(contacts, ';')) p++;
+ else p = contacts;
+
+ while(p)
+ {
+ // Processing line
+ strFirstName = strLastName = strNickname = strNick = strPhone = strGroup = strUin = strMail = NULL;
+ uin = 0;
+
+ // FirstName
+ if (p)
+ {
+ n = strchr(p, ';');
+ if (n && n != p) strFirstName = strndup(p, (n - p));
+ p = (n + 1);
+ }
+ // LastName
+ if (n && p)
+ {
+ n = strchr(p, ';');
+ if (n && n != p) strLastName = strndup(p, (n - p));
+ p = (n + 1);
+ }
+ // Nickname
+ if (n && p)
+ {
+ n = strchr(p, ';');
+ if (n && n != p) strNickname = strndup(p, (n - p));
+ p = (n + 1);
+ }
+ // Nick
+ if (n && p)
+ {
+ n = strchr(p, ';');
+ if (n && n != p) strNick = strndup(p, (n - p));
+ p = (n + 1);
+ }
+ // Phone
+ if (n && p)
+ {
+ n = strchr(p, ';');
+ if (n && n != p)
+ {
+ strPhone = (char*)malloc((n - p) + 5);
+ strncpy(strPhone, p, (n - p));
+ strcpy((strPhone + (n - p)), " SMS"); // Add SMS postfix
+ }
+ p = (n + 1);
+ }
+ // Group
+ if (n && p)
+ {
+ n = strchr(p, ';');
+ if (n && n != p) strGroup = strndup(p, (n - p));
+ p = (n + 1);
+ }
+ // Uin
+ if (n && p)
+ {
+ n = strchr(p, ';');
+ if (n && n != p)
+ {
+ strUin = strndup(p, (n - p));
+ uin = atoi(strUin);
+ }
+ p = (n + 1);
+ }
+ // Mail
+ if (n && p)
+ {
+ n = strchr(p, ';');
+ if (n && n != p) strMail = strndup(p, (n - p));
+ n = strchr(p, '\n');
+ p = (n + 1);
+ }
+ if (!n) p = NULL;
+
+ // Loadup contact
+ if (uin && strNick)
+ {
+ HANDLE hContact = getcontact(uin, 1, 1, _A2T(strNick));
+#ifdef DEBUGMODE
+ netlog("gg_parsecontacts(): Found contact %d with nickname \"%s\".", uin, strNick);
+#endif
+ // Write group
+ if (hContact && strGroup)
+ db_set_s(hContact, "CList", "Group", CreateGroup(strGroup));
+
+ // Write misc data
+ if (hContact && strFirstName) db_set_s(hContact, m_szModuleName, "FirstName", strFirstName);
+ if (hContact && strLastName) db_set_s(hContact, m_szModuleName, "LastName", strLastName);
+ if (hContact && strPhone) db_set_s(hContact, "UserInfo", "MyPhone0", strPhone); // Store now in User Info
+ if (hContact && strMail) db_set_s(hContact, "UserInfo", "Mye-mail0", strMail); // Store now in User Info
+ }
+
+ // Release stuff
+ if (strFirstName) free(strFirstName);
+ if (strLastName) free(strLastName);
+ if (strNickname) free(strNickname);
+ if (strNick) free(strNick);
+ if (strPhone) free(strPhone);
+ if (strGroup) free(strGroup);
+ if (strUin) free(strUin);
+ if (strMail) free(strMail);
+ }
+}
+
+//////////////////////////////////////////////////////////
+// import from server
+
+INT_PTR GGPROTO::import_server(WPARAM wParam, LPARAM lParam)
+{
+ char *password;
+ uin_t uin;
+ DBVARIANT dbv;
+
+ // Check if connected
+ if (!isonline())
+ {
+ MessageBox(NULL,
+ TranslateT("You have to be connected before you can import/export contacts from/to server."),
+ m_tszUserName, MB_OK | MB_ICONSTOP
+ );
+ return 0;
+ }
+
+ // Readup password
+ if (!db_get_s(NULL, m_szModuleName, GG_KEY_PASSWORD, &dbv, DBVT_ASCIIZ))
+ {
+ CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM) dbv.pszVal);
+ password = _strdup(dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ else return 0;
+
+ if (!(uin = db_get_dw(NULL, m_szModuleName, GG_KEY_UIN, 0)))
+ return 0;
+
+ // Making contacts list
+ EnterCriticalSection(&sess_mutex);
+ if (gg_userlist_request(sess, GG_USERLIST_GET, NULL) == -1)
+ {
+ TCHAR error[128];
+ LeaveCriticalSection(&sess_mutex);
+ mir_sntprintf(error, SIZEOF(error), TranslateT("List cannot be imported because of error:\n\t%s"), _tcserror(errno));
+ MessageBox(NULL, error, m_tszUserName, MB_OK | MB_ICONSTOP);
+ netlog("gg_import_server(): Cannot import list because of \"%s\".", strerror(errno));
+ }
+ LeaveCriticalSection(&sess_mutex);
+ free(password);
+
+ return 0;
+}
+
+//////////////////////////////////////////////////////////
+// remove from server
+
+INT_PTR GGPROTO::remove_server(WPARAM wParam, LPARAM lParam)
+{
+ char *password;
+ uin_t uin;
+ DBVARIANT dbv;
+
+ // Check if connected
+ if (!isonline())
+ {
+ MessageBox(NULL,
+ TranslateT("You have to be connected before you can import/export contacts from/to server."),
+ m_tszUserName, MB_OK | MB_ICONSTOP
+ );
+ return 0;
+ }
+
+ // Readup password
+ if (!db_get_s(NULL, m_szModuleName, GG_KEY_PASSWORD, &dbv, DBVT_ASCIIZ))
+ {
+ CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM) dbv.pszVal);
+ password = _strdup(dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ else return 0;
+
+ if (!(uin = db_get_dw(NULL, m_szModuleName, GG_KEY_UIN, 0)))
+ return 0;
+
+ // Making contacts list
+ EnterCriticalSection(&sess_mutex);
+ if (gg_userlist_request(sess, GG_USERLIST_PUT, NULL) == -1)
+ {
+ TCHAR error[128];
+ LeaveCriticalSection(&sess_mutex);
+ mir_sntprintf(error, SIZEOF(error), TranslateT("List cannot be removeed because of error:\n\t%s"), strerror(errno));
+ MessageBox(NULL, error, m_tszUserName, MB_OK | MB_ICONSTOP);
+ netlog("gg_remove_server(): Cannot remove list because of \"%s\".", strerror(errno));
+ }
+ LeaveCriticalSection(&sess_mutex);
+
+ // Set list removal
+ is_list_remove = TRUE;
+ free(password);
+
+ return 0;
+}
+
+INT_PTR GGPROTO::import_text(WPARAM wParam, LPARAM lParam)
+{
+ TCHAR str[MAX_PATH];
+ TCHAR filter[512], *pfilter;
+ struct _stat st;
+ FILE *f;
+
+ OPENFILENAME ofn = {0};
+ ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+ _tcsncpy(filter, TranslateT("Text files"), SIZEOF(filter));
+ _tcsncat(filter, _T(" (*.txt)"), SIZEOF(filter) - _tcslen(filter));
+ pfilter = filter + _tcslen(filter) + 1;
+ if (pfilter >= filter + SIZEOF(filter))
+ return 0;
+
+ _tcsncpy(pfilter, _T("*.TXT"), SIZEOF(filter) - (pfilter - filter));
+ pfilter = pfilter + _tcslen(pfilter) + 1;
+ if (pfilter >= filter + SIZEOF(filter))
+ return 0;
+ _tcsncpy(pfilter, TranslateT("All Files"), SIZEOF(filter) - (pfilter - filter));
+ _tcsncat(pfilter, _T(" (*)"), SIZEOF(filter) - (pfilter - filter) - _tcslen(pfilter));
+ pfilter = pfilter + _tcslen(pfilter) + 1;
+
+ if (pfilter >= filter + SIZEOF(filter))
+ return 0;
+
+ _tcsncpy(pfilter, _T("*"), SIZEOF(filter) - (pfilter - filter));
+ pfilter = pfilter + _tcslen(pfilter) + 1;
+ if (pfilter >= filter + SIZEOF(filter))
+ return 0;
+
+ *pfilter = '\0';
+ ofn.lpstrFilter = filter;
+ ofn.lpstrFile = str;
+ ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
+ ofn.nMaxFile = sizeof(str);
+ ofn.nMaxFileTitle = MAX_PATH;
+ ofn.lpstrDefExt = _T("txt");
+
+#ifdef DEBUGMODE
+ netlog("gg_import_text()");
+#endif
+ if (!GetOpenFileName(&ofn)) return 0;
+
+ f = _tfopen(str, _T("r"));
+ _tstat(str, &st);
+
+ if (f && st.st_size)
+ {
+ char *contacts = (char*)malloc(st.st_size * sizeof(char));
+ fread(contacts, sizeof(char), st.st_size, f);
+ fclose(f);
+ parsecontacts(contacts);
+ free(contacts);
+
+ MessageBox(NULL, TranslateT("List import successful."), m_tszUserName, MB_OK | MB_ICONINFORMATION);
+ }
+ else
+ {
+ TCHAR error[128];
+ mir_sntprintf(error, SIZEOF(error), TranslateT("List cannot be imported from file \"%s\" because of error:\n\t%s"), str, _tcserror(errno));
+ MessageBox(NULL, error, m_tszUserName, MB_OK | MB_ICONSTOP);
+ netlog("gg_import_text(): Cannot import list from file \"%S\" because of \"%s\".", str, strerror(errno));
+ }
+
+ return 0;
+}
+
+INT_PTR GGPROTO::export_text(WPARAM wParam, LPARAM lParam)
+{
+ TCHAR str[MAX_PATH];
+ OPENFILENAME ofn = {0};
+ TCHAR filter[512], *pfilter;
+ FILE *f;
+
+ _tcsncpy(str, TranslateT("contacts"), sizeof(str));
+ _tcsncat(str, _T(".txt"), sizeof(str) - _tcslen(str));
+
+ ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+ _tcsncpy(filter, TranslateT("Text files"), SIZEOF(filter));
+ _tcsncat(filter, _T(" (*.txt)"), SIZEOF(filter) - _tcslen(filter));
+ pfilter = filter + _tcslen(filter) + 1;
+ if (pfilter >= filter + SIZEOF(filter))
+ return 0;
+ _tcsncpy(pfilter, _T("*.TXT"), SIZEOF(filter) - (pfilter - filter));
+ pfilter = pfilter + _tcslen(pfilter) + 1;
+ if (pfilter >= filter + SIZEOF(filter))
+ return 0;
+ _tcsncpy(pfilter, TranslateT("All Files"), SIZEOF(filter) - (pfilter - filter));
+ _tcsncat(pfilter, _T(" (*)"), SIZEOF(filter) - (pfilter - filter) - _tcslen(pfilter));
+ pfilter = pfilter + _tcslen(pfilter) + 1;
+ if (pfilter >= filter + SIZEOF(filter))
+ return 0;
+ _tcsncpy(pfilter, _T("*"), SIZEOF(filter) - (pfilter - filter));
+ pfilter = pfilter + _tcslen(pfilter) + 1;
+ if (pfilter >= filter + SIZEOF(filter))
+ return 0;
+ *pfilter = '\0';
+ ofn.lpstrFilter = filter;
+ ofn.lpstrFile = str;
+ ofn.Flags = OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY;
+ ofn.nMaxFile = sizeof(str);
+ ofn.nMaxFileTitle = MAX_PATH;
+ ofn.lpstrDefExt = _T("txt");
+
+#ifdef DEBUGMODE
+ netlog("gg_export_text(%s).", str);
+#endif
+ if (!GetSaveFileName(&ofn)) return 0;
+
+ if (f = _tfopen(str, _T("w"))) {
+ char *contacts = gg_makecontacts(this, 0);
+ fwrite(contacts, sizeof(char), strlen(contacts), f);
+ fclose(f);
+ free(contacts);
+
+ MessageBox(NULL, TranslateT("List export successful."), m_tszUserName, MB_OK | MB_ICONINFORMATION);
+ }
+ else
+ {
+ TCHAR error[128];
+ mir_sntprintf(error, SIZEOF(error), TranslateT("List cannot be exported to file \"%s\" because of error:\n\t%s"), str, _tcserror(errno));
+ MessageBox(NULL, error, m_tszUserName, MB_OK | MB_ICONSTOP);
+ netlog("gg_import_text(): Cannot export list to file \"%s\" because of \"%s\".", str, strerror(errno));
+ }
+
+ return 0;
+}
+
+//////////////////////////////////////////////////////////
+// export to server
+
+INT_PTR GGPROTO::export_server(WPARAM wParam, LPARAM lParam)
+{
+ char *password, *contacts;
+ uin_t uin;
+ DBVARIANT dbv;
+
+ // Check if connected
+ if (!isonline())
+ {
+ MessageBox(NULL,
+ TranslateT("You have to be connected before you can import/export contacts from/to server."),
+ m_tszUserName, MB_OK | MB_ICONSTOP
+ );
+ return 0;
+ }
+
+ // Readup password
+ if (!db_get_s(NULL, m_szModuleName, GG_KEY_PASSWORD, &dbv, DBVT_ASCIIZ))
+ {
+ CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM) dbv.pszVal);
+ password = _strdup(dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ else return 0;
+
+ if (!(uin = db_get_dw(NULL, m_szModuleName, GG_KEY_UIN, 0)))
+ return 0;
+
+ // Making contacts list
+ contacts = gg_makecontacts(this, 1);
+
+#ifdef DEBUGMODE
+ netlog("gg_userlist_request(%s).", contacts);
+#endif
+
+ EnterCriticalSection(&sess_mutex);
+ if (gg_userlist_request(sess, GG_USERLIST_PUT, contacts) == -1)
+ {
+ TCHAR error[128];
+ LeaveCriticalSection(&sess_mutex);
+ mir_sntprintf(error, SIZEOF(error), TranslateT("List cannot be exported because of error:\n\t%s"), _tcserror(errno));
+ MessageBox(NULL, error, m_tszUserName, MB_OK | MB_ICONSTOP);
+ netlog("gg_export_server(): Cannot export list because of \"%s\".", strerror(errno));
+ }
+ LeaveCriticalSection(&sess_mutex);
+
+ // Set list removal
+ is_list_remove = FALSE;
+ free(contacts);
+ free(password);
+
+ return 0;
+}
+
+//////////////////////////////////////////////////////////
+// Import menus and stuff
+
+void GGPROTO::import_init(HGENMENU hRoot)
+{
+ CLISTMENUITEM mi = {0};
+ char service[64];
+
+ mi.cbSize = sizeof(mi);
+ mi.flags = CMIF_ICONFROMICOLIB | CMIF_ROOTHANDLE;
+ mi.hParentMenu = hRoot;
+
+ // Import from server item
+ mir_snprintf(service, sizeof(service), GGS_IMPORT_SERVER, m_szModuleName);
+ createObjService(service, &GGPROTO::import_server);
+ mi.position = 2000500001;
+ mi.icolibItem = GetIconHandle(IDI_IMPORT_SERVER);
+ mi.pszName = LPGEN("Import List From &Server");
+ mi.pszService = service;
+ hMainMenu[2] = Menu_AddProtoMenuItem(&mi);
+
+ // Import from textfile
+ mir_snprintf(service, sizeof(service), GGS_IMPORT_TEXT, m_szModuleName);
+ createObjService(service, &GGPROTO::import_text);
+ mi.position = 2000500002;
+ mi.icolibItem = GetIconHandle(IDI_IMPORT_TEXT);
+ mi.pszName = LPGEN("Import List From &Text File...");
+ mi.pszService = service;
+ hMainMenu[3] = Menu_AddProtoMenuItem(&mi);
+
+ // Remove from server
+ mir_snprintf(service, sizeof(service), GGS_REMOVE_SERVER, m_szModuleName);
+ createObjService(service, &GGPROTO::remove_server);
+ mi.position = 2000500003;
+ mi.icolibItem = GetIconHandle(IDI_REMOVE_SERVER);
+ mi.pszName = LPGEN("&Remove List From Server");
+ mi.pszService = service;
+ hMainMenu[4] = Menu_AddProtoMenuItem(&mi);
+
+ // Export to server
+ mir_snprintf(service, sizeof(service), GGS_EXPORT_SERVER, m_szModuleName);
+ createObjService(service, &GGPROTO::export_server);
+ mi.position = 2005000001;
+ mi.icolibItem = GetIconHandle(IDI_EXPORT_SERVER);
+ mi.pszName = LPGEN("Export List To &Server");
+ mi.pszService = service;
+ hMainMenu[5] = Menu_AddProtoMenuItem(&mi);
+
+ // Export to textfile
+ mir_snprintf(service, sizeof(service), GGS_EXPORT_TEXT, m_szModuleName);
+ createObjService(service, &GGPROTO::export_text);
+ mi.position = 2005000002;
+ mi.icolibItem = GetIconHandle(IDI_EXPORT_TEXT);
+ mi.pszName = LPGEN("Export List To &Text File...");
+ mi.pszService = service;
+ hMainMenu[6] = Menu_AddProtoMenuItem(&mi);
+}