/*
Weather Protocol plugin for Miranda IM
Copyright (c) 2012 Miranda NG Team
Copyright (c) 2005-2011 Boris Krasnovskiy All Rights Reserved
Copyright (c) 2002-2005 Calvin Che
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; version 2
of the License.
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 .
*/
/*
This file contain the source related loading, obtaining, and
saving individual weather data for a weather contact.
*/
#include "weather.h"
//============ LOAD WEATHER INFO FROM A CONTACT ============
// get station ID from DB
// hContact = the current contact handle
// return value = the string for station ID
void GetStationID(MCONTACT hContact, TCHAR* id, size_t idlen)
{
// accessing the database
if (DBGetStaticString(hContact, WEATHERPROTONAME, "ID", id, idlen))
id[0] = 0;
}
// initialize weather info by loading values from database
// hContact = current contact handle
// return value = the current weather information in WEATHERINFO struct
WEATHERINFO LoadWeatherInfo(MCONTACT hContact)
{
// obtaining values from the DB
// assuming station ID must exist at all time, but others does not have to
// if the string is not found in database, a value of "N/A" is stored in the field
WEATHERINFO winfo;
winfo.hContact = hContact;
GetStationID(hContact, winfo.id, SIZEOF(winfo.id));
if (DBGetStaticString(hContact, WEATHERPROTONAME, "Nick", winfo.city, SIZEOF(winfo.city)))
_tcsncpy(winfo.city, NODATA, SIZEOF(winfo.city) - 1);
if (DBGetStaticString(hContact, WEATHERCONDITION, "Update", winfo.update, SIZEOF(winfo.update)))
_tcsncpy(winfo.update, NODATA, SIZEOF(winfo.update) - 1);
if (DBGetStaticString(hContact, WEATHERCONDITION, "Condition", winfo.cond, SIZEOF(winfo.cond)))
_tcsncpy(winfo.cond, NODATA, SIZEOF(winfo.cond) - 1);
if (DBGetStaticString(hContact, WEATHERCONDITION, "Temperature", winfo.temp, SIZEOF(winfo.temp)))
_tcsncpy(winfo.temp, NODATA, SIZEOF(winfo.temp) - 1);
if (DBGetStaticString(hContact, WEATHERCONDITION, "High", winfo.high, SIZEOF(winfo.high)))
_tcsncpy(winfo.high, NODATA, SIZEOF(winfo.high) - 1);
if (DBGetStaticString(hContact, WEATHERCONDITION, "Low", winfo.low, SIZEOF(winfo.low)))
_tcsncpy(winfo.low, NODATA, SIZEOF(winfo.low) - 1);
if (DBGetStaticString(hContact, WEATHERCONDITION, "Sunset", winfo.sunset, SIZEOF(winfo.sunset)))
_tcsncpy(winfo.sunset, NODATA, SIZEOF(winfo.sunset) - 1);
if (DBGetStaticString(hContact, WEATHERCONDITION, "Sunrise", winfo.sunrise, SIZEOF(winfo.sunrise)))
_tcsncpy(winfo.sunrise, NODATA, SIZEOF(winfo.sunrise) - 1);
if (DBGetStaticString(hContact, WEATHERCONDITION, "Wind Speed", winfo.wind, SIZEOF(winfo.wind)))
_tcsncpy(winfo.wind, NODATA, SIZEOF(winfo.wind) - 1);
if (DBGetStaticString(hContact, WEATHERCONDITION, "Wind Direction", winfo.winddir, SIZEOF(winfo.winddir)))
_tcsncpy(winfo.winddir, NODATA, SIZEOF(winfo.winddir) - 1);
if (DBGetStaticString(hContact, WEATHERCONDITION, "Dewpoint", winfo.dewpoint, SIZEOF(winfo.dewpoint)))
_tcsncpy(winfo.dewpoint, NODATA, SIZEOF(winfo.dewpoint) - 1);
if (DBGetStaticString(hContact, WEATHERCONDITION, "Pressure", winfo.pressure, SIZEOF(winfo.pressure)))
_tcsncpy(winfo.pressure, NODATA, SIZEOF(winfo.pressure) - 1);
if (DBGetStaticString(hContact, WEATHERCONDITION, "Visibility", winfo.vis, SIZEOF(winfo.vis)))
_tcsncpy(winfo.vis, NODATA, SIZEOF(winfo.vis) - 1);
if (DBGetStaticString(hContact, WEATHERCONDITION, "Humidity", winfo.humid, SIZEOF(winfo.humid)))
_tcsncpy(winfo.humid, NODATA, SIZEOF(winfo.humid) - 1);
if (DBGetStaticString(hContact, WEATHERCONDITION, "Feel", winfo.feel, SIZEOF(winfo.feel)))
_tcsncpy(winfo.feel, NODATA, SIZEOF(winfo.feel) - 1);
winfo.status = (WORD)db_get_w(hContact, WEATHERPROTONAME, "StatusIcon", ID_STATUS_OFFLINE);
return winfo;
}
// getting weather setting from database
// return 0 on success
int DBGetData(MCONTACT hContact, char *setting, DBVARIANT *dbv)
{
if ( db_get_ts(hContact, WEATHERCONDITION, setting, dbv)) {
size_t len = mir_strlen(setting) + 1;
char *set = (char*)alloca(len + 1);
*set = '#';
memcpy(set + 1, setting, len);
if ( db_get_ts(hContact, WEATHERCONDITION, set, dbv))
return 1;
}
return 0;
}
int DBGetStaticString(MCONTACT hContact, const char *szModule, const char *valueName, TCHAR *dest, size_t dest_len)
{
DBVARIANT dbv;
if ( db_get_ts( hContact, szModule, valueName, &dbv ))
return 1;
_tcsncpy( dest, dbv.ptszVal, dest_len );
dest[ dest_len-1 ] = 0;
db_free( &dbv );
return 0;
}
//============ ERASE OLD SETTINGS ============
// erase all current weather information from database
// lastver = the last used version number in dword (using PLUGIN_MAKE_VERSION)
void EraseAllInfo()
{
TCHAR str[255];
int ContactCount = 0;
MCONTACT LastContact = NULL;
DBVARIANT dbv;
// loop through all contacts
for (MCONTACT hContact = db_find_first(WEATHERPROTONAME); hContact; hContact = db_find_next(hContact, WEATHERPROTONAME)) {
db_set_w(hContact,WEATHERPROTONAME, "Status",ID_STATUS_OFFLINE);
db_set_w(hContact,WEATHERPROTONAME, "StatusIcon",ID_STATUS_OFFLINE);
db_unset(hContact, "CList", "MyHandle");
// clear all data
if ( db_get_ts(hContact, WEATHERPROTONAME, "Nick", &dbv)) {
db_set_ts(hContact, WEATHERPROTONAME, "Nick", TranslateT(""));
db_set_s(hContact, WEATHERPROTONAME, "LastLog", "never");
db_set_s(hContact, WEATHERPROTONAME, "LastCondition", "None");
db_set_s(hContact, WEATHERPROTONAME, "LastTemperature", "None");
}
else db_free(&dbv);
DBDataManage(hContact, WDBM_REMOVE, 0, 0);
db_set_s(hContact, "UserInfo", "MyNotes", "");
// reset update tag
db_set_b(hContact,WEATHERPROTONAME, "IsUpdated",FALSE);
// reset logging settings
if ( !db_get_ts(hContact,WEATHERPROTONAME, "Log", &dbv)) {
db_set_b(hContact,WEATHERPROTONAME, "File",(BYTE)(dbv.ptszVal[0] != 0));
db_free(&dbv);
}
else db_set_b(hContact,WEATHERPROTONAME, "File",FALSE);
// if no default station find, assign a new one
if (opt.Default[0] == 0) {
GetStationID(hContact, opt.Default, SIZEOF(opt.Default));
opt.DefStn = hContact;
if ( !db_get_ts(hContact,WEATHERPROTONAME, "Nick",&dbv)) {
mir_sntprintf(str, SIZEOF(str), TranslateT("%s is now the default weather station"), dbv.ptszVal);
db_free(&dbv);
MessageBox(NULL, str, TranslateT("Weather Protocol"), MB_OK|MB_ICONINFORMATION);
}
}
// get the handle of the default station
if (opt.DefStn == NULL) {
if ( !db_get_ts(hContact,WEATHERPROTONAME, "ID",&dbv)) {
if ( !mir_tstrcmp(dbv.ptszVal, opt.Default))
opt.DefStn = hContact;
db_free(&dbv);
}
}
ContactCount++; // increment counter
LastContact = hContact;
}
// if weather contact exists, set the status to online so it is ready for update
// if (ContactCount != 0) status = ONLINE;
// in case where the default station is missing
if (opt.DefStn == NULL && ContactCount != 0) {
if ( !db_get_ts(LastContact, WEATHERPROTONAME, "ID", &dbv)) {
_tcsncpy(opt.Default, dbv.ptszVal, SIZEOF(opt.Default) - 1);
db_free(&dbv);
}
opt.DefStn = LastContact;
if ( !db_get_ts(LastContact,WEATHERPROTONAME, "Nick",&dbv)) {
mir_sntprintf(str, SIZEOF(str), TranslateT("%s is now the default weather station"), dbv.ptszVal);
db_free(&dbv);
MessageBox(NULL, str, TranslateT("Weather Protocol"), MB_OK|MB_ICONINFORMATION);
}
}
// save option in case of default station changed
db_set_ts(NULL, WEATHERPROTONAME, "Default", opt.Default);
}
void ConvertDataValue(WIDATAITEM *UpdateData, TCHAR *Data)
{
TCHAR str[MAX_DATA_LEN];
// convert the unit
if ( mir_tstrcmp(Data, TranslateT("")) && mir_tstrcmp(Data, NODATA) && mir_tstrcmp(Data, TranslateTS(NODATA))) {
// temperature
if ( !mir_tstrcmp(UpdateData->Name, _T("Temperature")) || !mir_tstrcmp(UpdateData->Name, _T("High")) ||
!mir_tstrcmp(UpdateData->Name, _T("Low")) || !mir_tstrcmp(UpdateData->Name, _T("Feel")) ||
!mir_tstrcmp(UpdateData->Name, _T("Dewpoint")) ||
!_tcsicmp(UpdateData->Unit, _T("C")) || !_tcsicmp(UpdateData->Unit, _T("F")) ||
!_tcsicmp(UpdateData->Unit, _T("K")))
{
GetTemp(Data, UpdateData->Unit, str);
_tcscpy(Data, str);
}
// pressure
else if ( !mir_tstrcmp(UpdateData->Name, _T("Pressure")) || !_tcsicmp(UpdateData->Unit, _T("HPA")) ||
!_tcsicmp(UpdateData->Unit, _T("KPA")) || !_tcsicmp(UpdateData->Unit, _T("MB")) ||
!_tcsicmp(UpdateData->Unit, _T("TORR")) || !_tcsicmp(UpdateData->Unit, _T("IN")) ||
!_tcsicmp(UpdateData->Unit, _T("MM")))
{
GetPressure(Data, UpdateData->Unit, str);
_tcscpy(Data, str);
}
// speed
else if ( !mir_tstrcmp(UpdateData->Name, _T("Wind Speed")) || !_tcsicmp(UpdateData->Unit, _T("KM/H")) ||
!_tcsicmp(UpdateData->Unit, _T("M/S")) || !_tcsicmp(UpdateData->Unit, _T("MPH")) ||
!_tcsicmp(UpdateData->Unit, _T("KNOTS")))
{
GetSpeed(Data, UpdateData->Unit, str);
_tcscpy(Data, str);
}
// visibility
else if ( !mir_tstrcmp(UpdateData->Name, _T("Visibility")) || !_tcsicmp(UpdateData->Unit, _T("KM")) ||
!_tcsicmp(UpdateData->Unit, _T("MILES")))
{
GetDist(Data, UpdateData->Unit, str);
_tcscpy(Data, str);
}
// elevation
else if ( !mir_tstrcmp(UpdateData->Name, _T("Elevation")) || !_tcsicmp(UpdateData->Unit, _T("FT")) ||
!_tcsicmp(UpdateData->Unit, _T("M")))
{
GetElev(Data, UpdateData->Unit, str);
_tcscpy(Data, str);
}
// converting case for condition to the upper+lower format
else if ( !_tcsicmp(UpdateData->Unit, _T("COND")))
CaseConv(Data);
// degree sign
else if ( !_tcsicmp(UpdateData->Unit, _T("DEG")))
{
if ( !opt.DoNotAppendUnit) _tcscat(Data, opt.DegreeSign);
}
// percent sign
else if ( !_tcsicmp(UpdateData->Unit, _T("%")))
{
if ( !opt.DoNotAppendUnit) _tcscat(Data, _T("%"));
}
// truncating strings for day/month to 2 or 3 characters
else if ( !_tcsicmp(UpdateData->Unit, _T("DAY")) || !_tcsicmp(UpdateData->Unit, _T("MONTH")))
if (opt.dUnit > 1 && mir_tstrlen(Data) > opt.dUnit)
Data[opt.dUnit] = '\0';
}
}
//============ GET THE VALUE OF A DATAITEM ============
// get the value of the data using the start, end strings
// UpdateData = the WIDATAITEM struct containing start, end, unit
// Data = the string containing weather data obtained from UpdateData
// global var. used: szInfo = the downloaded string
void GetDataValue(WIDATAITEM *UpdateData, TCHAR *Data, TCHAR** szData)
{
TCHAR last = 0, current, *start, *end;
unsigned startloc = 0, endloc = 0, respos = 0;
BOOL tag = FALSE, symb = FALSE;
TCHAR *szInfo = *szData;
Data[0] = 0;
// parse the data if available
if (UpdateData->Start[0] == 0 && UpdateData->End[0] == 0) return;
start = szInfo;
// the start string must be found
if (UpdateData->Start[0] != 0) {
start = _tcsstr(szInfo, UpdateData->Start);
if (start != NULL) {
// set the starting location for getting data
start += mir_tstrlen(UpdateData->Start);
szInfo = start;
}
}
// the end string must be found too
if (UpdateData->End[0] != 0)
end = _tcsstr(szInfo, UpdateData->End);
else
end = _tcsstr(szInfo, _T(" "));
if (end != NULL) {
// set the ending location
startloc = 0;
endloc = end - szInfo;
end += mir_tstrlen(UpdateData->End);
last = '\n';
}
// ignore if not both of the string found - this prevent crashes
if (start != NULL && end != NULL) {
// begin reading the data from start location to end location
// remove all HTML tag in between, as well as leading space, ending space,
// multiple spaces, tabs, and return key
while (startloc < endloc) {
if (szInfo[startloc] == '<') tag = TRUE;
else if (szInfo[startloc] == '&' &&
(szInfo[startloc+1] == ';' || szInfo[startloc+2] == ';' || szInfo[startloc+3] == ';' ||
szInfo[startloc+4] == ';' || szInfo[startloc+5] == ';' || szInfo[startloc+6] == ';'))
{
// ...but do NOT strip −
if ((endloc - startloc) > 7 &&_tcsncmp(szInfo + startloc, _T("−"), 7) == 0)
{
Data[respos++] = '-';
startloc += 7;
continue;
}
symb = TRUE;
}
else if (szInfo[startloc] == '>') tag = FALSE;
else if (szInfo[startloc] == ';') symb = FALSE;
else {
if ( !tag && !symb) {
current = szInfo[startloc];
if (current == '\n' || current == '\t' || current == ' ' || current == '\r')
current = ' ';
if (current != ' ' || last != ' ') {
if (last != '\n' && (respos != 0 || (respos == 0 && last != ' ')))
Data[respos++] = last;
last = current;
}
}
}
++startloc;
// prevent crashes if the string go over maximun length -> generate an error
if (respos >= MAX_DATA_LEN) {
if (opt.ShowWarnings && UpdateData->Name[0] != 0 && mir_tstrcmp(UpdateData->Name, _T("Ignore"))) {
mir_sntprintf(Data, MAX_DATA_LEN, TranslateT("Error when obtaining data: %s"), UpdateData->Name);
WPShowMessage(Data, SM_WARNING);
}
_tcsncpy(Data, TranslateT(""), MAX_DATA_LEN);
last = ' ';
respos = MAX_DATA_LEN - 1;
break;
}
}
// get the last character
if (last != ' ')
Data[respos++] = last;
// null terminate the string
Data[respos] = 0;
// convert the unit
ConvertDataValue(UpdateData, Data);
// remove the string before the data from szInfo
szInfo = end;
}
*szData = szInfo;
}
//============ ALLOCATE SPACE AND COPY STRING ============
// copy a string into a new memory location
// Data = the field the data is copied to
// Value = the original string, the string where data is copied from
void wSetData(char **Data, const char *Value)
{
if (Value[0] != 0) {
char *newData = (char*)mir_alloc(mir_strlen(Value) + 3);
strcpy(newData, Value);
*Data = newData;
}
else *Data = "";
}
void wSetData(WCHAR **Data, const char *Value)
{
if (Value[0] != 0)
*Data = mir_a2u(Value);
else
*Data = L"";
}
void wSetData(WCHAR **Data, const WCHAR *Value)
{
if (Value[0] != 0)
*Data = mir_wstrdup(Value);
else
*Data = L"";
}
// A safer free function that free memory for a string
// Data = the string occuping the data to be freed
void wfree(char **Data)
{
if (*Data && mir_strlen(*Data) > 0)
mir_free(*Data);
*Data = NULL;
}
void wfree(WCHAR **Data)
{
if (*Data && wcslen(*Data) > 0)
mir_free(*Data);
*Data = NULL;
}
//============ MANAGE THE ITEMS STORED IN DB ============
// get single setting that is found
// szSetting = the setting name
// lparam = the counter
int GetWeatherDataFromDB(const char *szSetting, LPARAM lparam)
{
LIST *pList = (LIST*)lparam;
pList->insert(mir_strdup(szSetting));
return 0;
}
// remove or display the weather information for a contact
// hContact - the contact in which the info is going to be removed
void DBDataManage(MCONTACT hContact, WORD Mode, WPARAM wParam, LPARAM lParam)
{
LIST arSettings(10);
// get all the settings and stored them in a temporary list
DBCONTACTENUMSETTINGS dbces;
dbces.lParam = (LPARAM)&arSettings;
dbces.pfnEnumProc = GetWeatherDataFromDB;
dbces.szModule = WEATHERCONDITION;
CallService(MS_DB_CONTACT_ENUMSETTINGS, hContact, (LPARAM)&dbces);
// begin deleting settings
for (int i = arSettings.getCount() - 1; i >= 0; i--) {
char *szSetting = arSettings[i];
DBVARIANT dbv;
if (!db_get_ts(hContact, WEATHERCONDITION, szSetting, &dbv)) {
switch (Mode) {
case WDBM_REMOVE:
db_unset(hContact, WEATHERCONDITION, szSetting);
break;
case WDBM_DETAILDISPLAY:
// skip the "WeatherInfo" variable
if (!mir_strcmp(szSetting, "WeatherInfo") || !mir_strcmp(szSetting, "Ignore") || szSetting[0] == '#')
continue;
HWND hList = GetDlgItem((HWND)wParam, IDC_DATALIST);
LV_ITEM lvi = { 0 };
lvi.mask = LVIF_TEXT | LVIF_PARAM;
lvi.lParam = i;
lvi.pszText = TranslateTS(_A2T(szSetting));
lvi.iItem = ListView_InsertItem(hList, &lvi);
lvi.pszText = dbv.ptszVal;
ListView_SetItemText(hList, lvi.iItem, 1, dbv.ptszVal);
break;
}
db_free(&dbv);
}
mir_free(szSetting);
}
}