|
- |
-WebTV
-A moderated discussion with WebTV customers
-
-Hacking
-Not grandma friendly
-
-4x4s
-The on and off-road four wheel drive vehicle
-
- |
- |
-WebTV Plus
-bf0 is for bitches and BPS is boring
-
-MIDIs
-The best music format
-
-HTML
-Every timeline starts with HTML
-
+ | `;
+
+var limit = 6;
+while (featuredGroups.length > limit) featuredGroups.pop(); // remove anything passing our limit
+
+function printGroup(group) {
+ return `${group.name} ${group.description}
 `;
+}
+
+// evens
+Object.keys(featuredGroups).forEach((k) => { if (k % 2 == 0) { data += printGroup(featuredGroups[k]); } });
+
+if (featuredGroups.length > 1) data += ` | | `;
+
+// odds
+Object.keys(featuredGroups).forEach((k) => { if (k % 2 != 0) data += printGroup(featuredGroups[k]); });
+
+
+data += `
diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-news/news.js b/zefie_wtvp_minisrv/ServiceVault/wtv-news/news.js
index 562dffdf..5d065808 100644
--- a/zefie_wtvp_minisrv/ServiceVault/wtv-news/news.js
+++ b/zefie_wtvp_minisrv/ServiceVault/wtv-news/news.js
@@ -1,6 +1,5 @@
var minisrv_service_file = true;
-
-console.log('f')
+const wtvnews = new WTVNews(minisrv_config, service_name);
async function throwError(e) {
var errpage = wtvshared.doErrorPage(400, null, e.toString());
@@ -8,15 +7,30 @@ async function throwError(e) {
}
+function isToday (chkdate) {
+ const today = new Date()
+ return chkdate.getDate() == today.getDate() &&
+ chkdate.getMonth() == today.getMonth() &&
+ chkdate.getFullYear() == today.getFullYear()
+}
+
async function WebTVListGroup(group) {
+ var page_limit_default = 100;
wtvnews.connectUsenet().then(() => {
wtvnews.selectGroup(group).then(() => {
- wtvnews.listGroup(group).then((response) => {
+ var limit_per_page = (request_headers.query.limit) ? parseInt(request_headers.query.limit) : page_limit_default;
+ var page = (request_headers.query.chunk) ? parseInt(request_headers.query.chunk) : 0;
+
+ wtvnews.listGroup(group, page, limit_per_page).then((response) => {
if (response.code == 211) {
NGCount = response.group.number;
NGArticles = response.group.articleNumbers;
+ page_start = (limit_per_page * page) + 1;
+ page_end = (page + 1) * limit_per_page;
+ if (page_end > NGCount) page_end = NGCount;
wtvnews.getHeaderObj(NGArticles).then((messages) => {
+ messages = wtvnews.sortByResponse(messages);
wtvnews.quitUsenet();
headers = `200 OK
Connection: Keep-Alive
@@ -30,6 +44,7 @@ top.location="news:${request_headers.query.group}";
${request_headers.query.group}
+
@@ -56,7 +71,7 @@ top.location="news:${request_headers.query.group}";
|
|
-
@@ -103,15 +118,9 @@ cellspacing=0 cellpadding=0>
|
- |
+ | |
|
-
@@ -148,9 +157,37 @@ Group: ${request_headers.query.group}
+
+
+
+
+${(page > 0) ? ` ` : ` `}
+ |
+
+ |
+
+ |
+
+ |
+${(page_end < NGCount) ? ` ` : ` `}
+ |
+ |
+
+
+ |
+|
+${page_start}-${page_end}
+ |
+
+
+ |
+|
+ | |
+
-|
+ |
+ |
@@ -165,8 +202,10 @@ Group: ${request_headers.query.group}
if (NGCount > 0) {
Object.keys(messages).forEach(function (k) {
- var message = messages[k]
- var message_date = message.headers.DATE;
+ var message = messages[k].article;
+ var has_relation = (messages[k].relation !== null) ? true : false;
+ var date_obj = new Date(Date.parse(message.headers.DATE));
+ var date = (isToday(date_obj)) ? strftime("%I:%M %p", date_obj) : strftime("%b %d", date_obj)
data += `
@@ -174,13 +213,13 @@ Group: ${request_headers.query.group}
+${(has_relation) ? `| ` : ''}
|
${(message.headers.SUBJECT) ? message.headers.SUBJECT : "(No Subject)"}
|
-${message.headers.FROM}, ${message.headers.DATE}
-
+${(message.headers.FROM.indexOf(' ') > 0) ? message.headers.FROM.split(' ')[0] : message.headers.FROM}, ${date}
| |
|
| `;
@@ -218,20 +257,20 @@ ${message.headers.FROM}, ${message.headers.DATE}
}).catch((e) => { throwError(e) });
}
-async function WebTVShowMessage(client, group, article) {
- var connected = await clientConnect(client)
- if (connected) {
- response = await selectGroup(client, group);
- if (response) {
- response = await getArticle(client, article);
- console.log(response);
- if (response.code == 220) {
- headers = `200 OK
-Content-type: text/html`;
+async function WebTVShowMessage(group, article) {
+ var article = parseInt(article);
+ wtvnews.connectUsenet().then(() => {
+ wtvnews.selectGroup(group).then((response) => {
+ wtvnews.getArticle(article).then((response) => {
+ wtvnews.quitUsenet();
+ if (response.code == 220) {
+ console.log(response);
+ headers = `200 OK
+Content-type: text/html
+wtv-expire-all: wtv-news:/news?group=${group}&article=`;
+ var message_colors = session_data.mailstore.defaultColors;
- var message_colors = session_data.mailstore.defaultColors;
-
- data = `
+ data = `
|
|
-
+
|
|
@@ -314,11 +353,11 @@ cellspacing=0 cellpadding=0>
|
|
-
+
|
|
@@ -384,7 +423,7 @@ cellspacing=0 cellpadding=0>
|
|
-
|
@@ -461,20 +500,19 @@ ${wtvshared.htmlEntitize(response.article.headers.NEWSGROUPS)}
|
|
Date: |
-${console.log(Date.parse(response.article.headers.DATE))}
-${strftime("%a, %b %e, %Y, %I:%M%P", new Date(Date.parse(response.article.headers.DATE) / 1000))}
+${strftime("%a, %b %e, %Y, %I:%M%P", new Date(Date.parse(response.article.headers.DATE)))}
|
|
From:
| `;
- // if (message.from_name != message.from_addr) {
- // data += `${wtvshared.htmlEntitize(message.from_addr)} `;
- // } else {
- data += `${wtvshared.htmlEntitize(response.article.headers.FROM)}`;
- // }
+ // if (message.from_name != message.from_addr) {
+ // data += `${wtvshared.htmlEntitize(message.from_addr)} `;
+ // } else {
+ data += `${wtvshared.htmlEntitize(response.article.headers.FROM)}`;
+ // }
- data += ` |
+ data += `
|
|
|
@@ -488,52 +526,51 @@ ${(response.article.headers.SUBJECT) ? wtvshared.htmlEntitize(response.article.h
|
`;
- var message_body = response.article.body.join("\n");
- data += `
+ var message_body = response.article.body.join("\n");
+ data += `
${wtvshared.htmlEntitize(message_body, true)}
`;
- data += `
-`;
- /*
- if (message.attachments) {
- message.attachments.forEach((v, k) => {
- if (v) {
- console.log("*****************", v['Content-Type']);
- switch (v['Content-Type']) {
- case "image/jpeg":
- data += `}&wtv-title=Video%20Snapshot)
`;
- break;
- case "audio/wav":
- data += `
- recording.wav (wav attachment)
- |
- |
-`;
- break;
+ data += "";
+ /*
+ if (message.attachments) {
+ message.attachments.forEach((v, k) => {
+ if (v) {
+ console.log("*****************", v['Content-Type']);
+ switch (v['Content-Type']) {
+ case "image/jpeg":
+ data += `}&wtv-title=Video%20Snapshot)
`;
+ break;
+ case "audio/wav":
+ data += `
+ recording.wav (wav attachment)
+ |
+ |
+ `;
+ break;
+ }
}
- }
- });
+ });
+ }
+ if (message.url) {
+ data += `Included Page: ${wtvshared.htmlEntitize(message.url_title).replace(/'/gi, "'")}`;
+ }
+ */
+ data += " | ";
+ sendToClient(socket, headers, data);
+
+ } else {
+ throwError("invalid response code. expected: 220, received:", response.code);
}
- if (message.url) {
- data += `Included Page: ${wtvshared.htmlEntitize(message.url_title).replace(/'/gi, "'")}`;
- }
- */
- data += `
- |
-
-
-`;
- sendToClient(socket, headers, data);
- } else {
- var errpage = wtvshared.doErrorPage(400, null, "No such article in group "+group+"");
- sendToClient(socket, errpage[0], errpage[1]);
- }
- } else {
- var errpage = wtvshared.doErrorPage(400, null, "No such group: "+group+"");
- sendToClient(socket, errpage[0], errpage[1]);
- }
- }
+ }).catch((e) => {
+ throwError(e);
+ });
+ }).catch((e) => {
+ throwError(e);
+ });
+ }).catch((e) => {
+ throwError(e);
+ });;
}
if (!minisrv_config.services[service_name].upstream_address || !minisrv_config.services[service_name].upstream_port) {
diff --git a/zefie_wtvp_minisrv/app.js b/zefie_wtvp_minisrv/app.js
index b4eaf918..029c5a83 100644
--- a/zefie_wtvp_minisrv/app.js
+++ b/zefie_wtvp_minisrv/app.js
@@ -164,7 +164,7 @@ var runScriptInVM = function (script_data, user_contextObj = {}, privileged = fa
contextObj[vm_modules[k]] = require(module_file);
modules_loaded.push(module_file)
} catch (e) {
- console.error(" *!* Could not load module", module_file, "requested by service", contextObj.service_name)
+ console.error(" *!* Could not load module", module_file, "requested by service", contextObj.service_name, e)
}
})
}
diff --git a/zefie_wtvp_minisrv/includes/WTVClientSessionData.js b/zefie_wtvp_minisrv/includes/WTVClientSessionData.js
index 4187369c..0a6c5cf1 100644
--- a/zefie_wtvp_minisrv/includes/WTVClientSessionData.js
+++ b/zefie_wtvp_minisrv/includes/WTVClientSessionData.js
@@ -427,7 +427,7 @@ class WTVClientSessionData {
}
generatePassword(len) {
- return CryptoJS.lib.WordArray.random(Math.round(len / 2)).toString(CryptoJS.enc.Hex);
+ return this.wtvshared.generatePassword(len);
}
setUserPassword(passwd) {
diff --git a/zefie_wtvp_minisrv/includes/WTVNews.js b/zefie_wtvp_minisrv/includes/WTVNews.js
index 352b3cba..72dd2b84 100644
--- a/zefie_wtvp_minisrv/includes/WTVNews.js
+++ b/zefie_wtvp_minisrv/includes/WTVNews.js
@@ -2,12 +2,16 @@ class WTVNews {
minisrv_config = null;
newsie = require('newsie').default;
+ strftime = require('strftime');
+ wtvshared = null;
service_name = null;
client = null;
constructor(minisrv_config, service_name) {
this.minisrv_config = minisrv_config;
this.service_name = service_name;
+ const { WTVShared } = require("./WTVShared.js");
+ this.wtvshared = new WTVShared(minisrv_config);
this.client = new this.newsie({
host: this.minisrv_config.services[service_name].upstream_address,
port: this.minisrv_config.services[service_name].upstream_port
@@ -27,13 +31,24 @@ class WTVNews {
});
}
- listGroup(group) {
+ listGroup(group, page = 0, limit = 100) {
return new Promise((resolve, reject) => {
- this.client.listGroup(group).then((data) => {
- resolve(data);
+ this.selectGroup(group).then((res) => {
+ var range = {
+ start: (limit * page) + res.group.low,
+ }
+ range.end = range.start + limit;
+ if (page) range.start++;
+ if (range.end > res.high) delete range.group.end;
+ console.log(res, range);
+ this.client.listGroup(group, range).then((data) => {
+ resolve(data);
+ }).catch((e) => {
+ console.error(" * WTVNews Error:", "Command: listGroup", e);
+ reject(`No such group ${group}`);
+ });
}).catch((e) => {
- console.error(" * WTVNews Error:", "Command: listGroup", e);
- reject(`No such group ${group}`);
+ console.error(" * WTVNews Error:", "Command: selectGroup", e);
});
})
}
@@ -41,7 +56,7 @@ class WTVNews {
selectGroup(group) {
return new Promise((resolve, reject) => {
this.client.group(group).then((response) => {
- if (response.code == 211) resolve(true);
+ if (response.code == 211) resolve(response);
else reject(`No such group ${group}`);
}).catch((e) => {
console.error(" * WTVNews Error:", "Command: selectGroup", e);
@@ -51,12 +66,50 @@ class WTVNews {
}
getArticle(articleID) {
+ var articleID = parseInt(articleID);
return new Promise((resolve, reject) => {
+ var promises = [];
this.client.article(articleID).then((data) => {
- resolve(data)
+ // ask server for next article
+ promises.push(new Promise((resolve, reject) => {
+ this.client.next().then((res) => {
+ data.next_article = res.article.articleNumber;
+ resolve(data.next_article);
+ }).catch(() => {
+ data.next_article = null;
+ resolve(data.next_article);
+ })
+ }));
+
+ // ask server for previous article
+ promises.push(new Promise((resolve, reject) => {
+ this.client.last().then((res) => {
+ data.prev_article = res.article.articleNumber;
+ // do it again
+ this.client.article(data.prev_article).then(() => {
+ this.client.last().then((res) => {
+ data.prev_article = res.article.articleNumber;
+ resolve(data.prev_article);
+ }).catch(() => {
+ data.prev_article = null;
+ resolve(data.prev_article);
+ });
+ }).catch(() => {
+ data.prev_article = null;
+ resolve(data.prev_article);
+ });
+ }).catch(() => {
+ data.prev_article = null;
+ resolve(data.prev_article);
+ })
+ }));
+
+ Promise.all(promises).then(() => {
+ resolve(data);
+ });
}).catch((e) => {
reject(`Error reading article ID ${articleID}`);
- console.error(" * WTVNews Error:", "Command: article", e);
+ console.error(" * WTVNews Error:", "Command: article", "args:", articleID, "Error:", e);
});
});
}
@@ -87,24 +140,193 @@ class WTVNews {
});
}
+ postToGroup(group, from_addr, msg_subject, msg_body, article = null) {
+ return new Promise((resolve, reject) => {
+ var promises = [];
+ var messageid = null;
+ this.connectUsenet()
+ .then(() => {
+ if (article) {
+ promises.push(new Promise((resolve, reject) => {
+ this.selectGroup(group).then((res) => {
+ this.getArticleMessageID(article).then((data) => {
+ console.log(data);
+ messageid = data;
+ resolve(data);
+ }).catch((e) => {
+ console.log(e);
+ reject(e)
+ });
+ }).catch((e) => {
+ console.log(e);
+ reject(e)
+ });
+ }));
+ }
+ Promise.all(promises).then(() => {
+ this.client.post()
+ .then((response) => {
+ if (response.code == 340) {
+ var articleData = {};
+ articleData.headers = {
+ 'Relay-Version': "version zefie_wtvp_minisrv " + this.minisrv_config.version + "; site " + this.minisrv_config.config.domain_name,
+ 'Posting-Version': "version zefie_wtvp_minisrv " + this.minisrv_config.version + "; site " + this.minisrv_config.config.domain_name,
+ 'Path': "@" + this.minisrv_config.config.domain_name,
+ 'From': from_addr,
+ 'Newsgroups': group,
+ 'Subject': msg_subject || "(No subject)",
+ 'Message-ID': "<" + this.wtvshared.generatePassword(16) + "@" + this.minisrv_config.config.domain_name + ">",
+ 'Date': this.strftime('%A, %d-%b-%y %k:%M:%S %z', new Date())
+ }
+ if (messageid) articleData.headers.References = messageid;
+
+ if (msg_body) {
+ articleData.body = msg_body.split("\n");
+ } else {
+ articleData.body = [];
+ }
+ console.log(articleData);
+ response.send(articleData).then((response) => {
+ this.client.quit();
+ if (response.code !== 240) {
+ reject("Could not send post. Server returned error " + response.code);
+ } else {
+ resolve(true);
+ }
+ }).catch((e) => {
+ this.client.quit();
+ reject("Could not send post. Server returned error " + response.code);
+ });
+ }
+ else {
+ this.client.quit();
+ console.log('usenet upstream uncaught error', e);
+ reject("Could not send post. Server returned unknown error");
+ };
+ }).catch((e) => {
+ console.log('could not connect to server', e);
+ reject("could not connect to server");
+ });
+ });
+ });
+ });
+ }
+
+ getArticleMessageID(articleID) {
+ return new Promise((resolve, reject) => {
+ this.client.article(articleID).then((data) => {
+ resolve(data.article.messageId);
+ }).catch((e) => {
+ reject(e);
+ });
+ });
+ }
+
getHeaderObj(NGArticles) {
return new Promise((resolve, reject) => {
var messages = [];
- var failed = false;
+ var promises = [];
for (var article in NGArticles) {
- if (failed) continue;
if (article == "getCaseInsensitiveKey") continue;
- this.getHeader(NGArticles[article]).then((data) => {
- if (data.article) messages.push(data.article)
+ promises.push(new Promise((resolve, reject) => {
+ this.getHeader(NGArticles[article]).then((data) => {
+ if (data.article) messages.push(data.article)
+ resolve();
+ }).catch((e) => {
+ console.log(e, article);
+ reject(e);
+ });
+ }));
+ }
+ if (promises.length > 0) {
+ Promise.all(promises).then(() => {
+ if (messages.length > 0) resolve(messages);
}).catch((e) => {
- console.log(e, article);
- failed = e;
+ reject("Could not read message list", e);
+ });
+ } else {
+ resolve(messages);
+ }
+ });
+ }
+
+ sortByResponse(messages) {
+ var sorted = [];
+ var message_id_roots = [];
+ var message_relations = [];
+ Object.keys(messages).forEach((k) => {
+ var messageId = messages[k].messageId;
+ var ref = messages[k].headers.REFERENCES;
+ if (ref) {
+ var res = message_id_roots.find(e => e.messageId == ref);
+ if (res) {
+ // see if its attached to a root post
+ if (message_relations[res.messageId]) message_relations[res.messageId].push({ "messageId": messageId, "index": k });
+ else message_relations[res.messageId] = [{ "messageId": messageId, "index": k }];
+ } else {
+ // see if its related to a relation
+ if (Object.keys(message_relations).length > 0) {
+ var found = false;
+ Object.keys(message_relations).forEach((j) => {
+ if (message_relations[j].length > 0) {
+ Object.keys(message_relations[j]).forEach((h) => {
+ if (found) return;
+ if (message_relations[j][h].messageId == ref) {
+ var searchref = messages[message_relations[j][h].index].headers.REFERENCES || null;
+ var mainref = null;
+ while (searchref !== null) {
+ var searchart = messages.find(e => e.messageId == searchref);
+ var searchref = searchart.headers.REFERENCES || null;
+ }
+ mainref = searchart.messageId;
+ message_relations[mainref].push({ "messageId": messageId, "index": k });
+ found = true;
+ }
+ });
+ }
+ })
+ if (!found) {
+ message_id_roots.push({ "messageId": messageId, "index": j });
+ }
+ } else {
+ message_id_roots.push({ "messageId": messageId, "index": k });
+ }
+ }
+ }
+ else {
+ message_id_roots.push({ "messageId": messageId, "index": k });
+ }
+ });
+
+ // sort the relations, putting root articles first, followed by their relations
+ var message_roots_sorted = [];
+ Object.keys(message_id_roots).forEach((k) => {
+ // sort relations by date
+ var article = messages[message_id_roots[k].index];
+ var article_date = Date.parse(article.headers.DATE);
+ message_roots_sorted.push({ "article": article, "relation": null, "date": article_date });
+ });
+ message_roots_sorted.sort((a, b) => { return (a.date > b.date) });
+ Object.keys(message_roots_sorted).forEach((k) => {
+ sorted.push(message_roots_sorted[k]);
+
+ if (message_relations[message_id_roots[k].messageId]) {
+ var relations = [];
+ Object.keys(message_relations[message_id_roots[k].messageId]).forEach((j) => {
+ // sort relations by date
+ var article = messages[message_relations[message_id_roots[k].messageId][j].index];
+ var article_date = Date.parse(article.headers.DATE);
+ relations.push({ "article": article, "relation": message_id_roots[k].messageId, "date": article_date })
+ });
+ relations.sort((a, b) => { return (a.date > b.date) });
+ Object.keys(relations).forEach((j) => {
+ sorted.push(relations[j]);
});
}
- if (!failed) resolve(messages);
- else reject("Could not read message list", failed);
- });
+ })
+ return sorted;
}
+
}
module.exports = WTVNews;
\ No newline at end of file
diff --git a/zefie_wtvp_minisrv/includes/WTVShared.js b/zefie_wtvp_minisrv/includes/WTVShared.js
index 995e08b9..4dcdb994 100644
--- a/zefie_wtvp_minisrv/includes/WTVShared.js
+++ b/zefie_wtvp_minisrv/includes/WTVShared.js
@@ -1,7 +1,7 @@
/**
* Shared functions across all classes and apps
*/
-
+const CryptoJS = require('crypto-js');
class WTVShared {
@@ -9,7 +9,6 @@ class WTVShared {
fs = require('fs');
v8 = require('v8');
zlib = require('zlib');
- CryptoJS = require('crypto-js');
html_entities = require('html-entities'); // used externally by service scripts
sanitizeHtml = require('sanitize-html');
iconv = require('iconv-lite');
@@ -310,6 +309,10 @@ class WTVShared {
}
}
+ generatePassword(len) {
+ return CryptoJS.lib.WordArray.random(Math.round(len / 2)).toString(CryptoJS.enc.Hex);
+ }
+
getMiniSrvConfig() {
return this.minisrv_config;
}
@@ -435,7 +438,7 @@ class WTVShared {
var post_obj = {};
post_obj.query = [];
try {
- var post_text = obj.post_data.toString(this.CryptoJS.enc.Utf8);
+ var post_text = obj.post_data.toString(CryptoJS.enc.Utf8);
if (post_text.length > 0) {
post_text = post_text.split("&");
for (let i = 0; i < post_text.length; i++) {
@@ -454,7 +457,7 @@ class WTVShared {
post_text = post_text.substring(0, post_text.length - 1);
obj.post_data = post_text.hexEncode();
} catch (e) {
- obj.post_data = obj.post_data.toString(this.CryptoJS.enc.Hex);
+ obj.post_data = obj.post_data.toString(CryptoJS.enc.Hex);
}
} else {
// simple, no filter
diff --git a/zefie_wtvp_minisrv/includes/config.json b/zefie_wtvp_minisrv/includes/config.json
index 39c2e065..ea196974 100644
--- a/zefie_wtvp_minisrv/includes/config.json
+++ b/zefie_wtvp_minisrv/includes/config.json
@@ -126,7 +126,10 @@
},
"wtv-mail": {
"port": 1608,
- "connections": 3
+ "connections": 3,
+ "modules": [
+ "WTVNews"
+ ]
},
"wtv-passport": {
"port": 1654
| | | |