Commit
Author: Manos Pitsidianakis [manos@pitsidianak.is]
Hash: e9a72072bfc989cffe3343195469dcb6d3041f4d
Timestamp: Sun, 08 Sep 2024 07:55:22 +0000 (4 months ago)

+5 -1119 +/-8 browse
Remove unused/obsolete plugins code and mentions
Remove unused/obsolete plugins code and mentions

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
1diff --git a/meli/docs/meli.1 b/meli/docs/meli.1
2index 856a30e..fb6daff 100644
3--- a/meli/docs/meli.1
4+++ b/meli/docs/meli.1
5 @@ -649,7 +649,7 @@ catchall for general errors
6 process panic
7 .El
8 .Sh ENVIRONMENT
9- .Bl -tag -width "$XDG_CONFIG_HOME/meli/plugins/*" -offset indent
10+ .Bl -tag -width "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" -offset indent
11 .It Ev EDITOR
12 Specifies the editor to use
13 .It Ev MELI_CONFIG
14 @@ -665,7 +665,7 @@ overrides this.
15 .Sh FILES
16 .Nm
17 uses the following parts of the XDG standard:
18- .Bl -tag -width "$XDG_CONFIG_HOME/meli/plugins/*" -offset indent
19+ .Bl -tag -width "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" -offset indent
20 .It Ev XDG_CONFIG_HOME
21 defaults to
22 .Pa ~/.config/
23 @@ -675,17 +675,13 @@ defaults to
24 .El
25 .Pp
26 and appropriates the following locations:
27- .Bl -tag -width "$XDG_CONFIG_HOME/meli/plugins/*" -offset indent
28+ .Bl -tag -width "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" -offset indent
29 .It Pa $XDG_CONFIG_HOME/meli/
30 User configuration directory
31 .It Pa $XDG_CONFIG_HOME/meli/config.toml
32 User configuration file, see
33 .Xr meli.conf 5
34 for its syntax and values.
35- .It Pa $XDG_CONFIG_HOME/meli/hooks/*
36- Reserved for event hooks.
37- .It Pa $XDG_CONFIG_HOME/meli/plugins/*
38- Reserved for plugin files.
39 .It Pa $XDG_CACHE_HOME/meli/*
40 Internal cached data used by meli.
41 .It Pa $XDG_DATA_HOME/meli/*
42 diff --git a/meli/src/plugins.rs b/meli/src/plugins.rs
43deleted file mode 100644
44index 103df5b..0000000
45--- a/meli/src/plugins.rs
46+++ /dev/null
47 @@ -1,303 +0,0 @@
48- /*
49- * meli - ui plugins
50- *
51- * Copyright 2019 Manos Pitsidianakis
52- *
53- * This file is part of meli.
54- *
55- * meli is free software: you can redistribute it and/or modify
56- * it under the terms of the GNU General Public License as published by
57- * the Free Software Foundation, either version 3 of the License, or
58- * (at your option) any later version.
59- *
60- * meli is distributed in the hope that it will be useful,
61- * but WITHOUT ANY WARRANTY; without even the implied warranty of
62- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
63- * GNU General Public License for more details.
64- *
65- * You should have received a copy of the GNU General Public License
66- * along with meli. If not, see <http://www.gnu.org/licenses/>.
67- */
68-
69- //! Plugins are executed by meli and communication is done by `messagepack` IPC.
70-
71- use melib::error::{Error, Result};
72- use std::collections::HashMap;
73- use std::io::Write;
74- use std::os::unix::net::{UnixListener, UnixStream};
75- use std::path::PathBuf;
76- use std::process::Stdio;
77- use uuid::Uuid;
78-
79- //pub mod backend;
80- pub mod rpc;
81- pub use rpc::*;
82-
83- pub const BACKEND_FN: i8 = 0;
84- pub const BACKEND_OP_FN: i8 = 1;
85-
86- #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
87- pub enum PluginKind {
88- LongLived,
89- Filter,
90- Backend,
91- }
92-
93- impl Default for PluginKind {
94- fn default() -> Self {
95- Self::LongLived
96- }
97- }
98-
99- #[derive(Default, Debug, Clone, Serialize, Deserialize)]
100- pub struct Plugin {
101- kind: PluginKind,
102- executable: String,
103- name: String,
104- #[serde(default)]
105- hooks: Vec<String>,
106- }
107-
108- impl Plugin {
109- pub fn kind(&self) -> PluginKind {
110- self.kind
111- }
112- }
113-
114- #[derive(Debug)]
115- pub struct PluginManager {
116- plugins: HashMap<String, Plugin>,
117- sessions: HashMap<Uuid, String>,
118- instances: HashMap<Uuid, std::process::Child>,
119- streams: HashMap<Uuid, RpcChannel>,
120- hooks: HashMap<String, UIHook>,
121- listener: UnixListener,
122- }
123-
124- fn socket_path() -> PathBuf {
125- xdg::BaseDirectories::new()
126- .and_then(|base_dirs| {
127- base_dirs
128- .place_runtime_file("meli-plugins")
129- .or_else(|_| base_dirs.place_cache_file("meli-plugins"))
130- .or_else(|_| {
131- let mut p = base_dirs.get_cache_home();
132- p.push("meli-plugins");
133- Ok(p)
134- })
135- })
136- .unwrap_or_else(|_| PathBuf::from("."))
137- }
138-
139- impl Drop for PluginManager {
140- fn drop(&mut self) {
141- let _ = std::fs::remove_file(&socket_path());
142- for (k, c) in self.instances.iter_mut() {
143- if let Err(err) = debug!(c.kill()) {
144- eprintln!(
145- "Error: could not kill process {} spawned by plugin {} ({})",
146- c.id(),
147- &self.plugins[&self.sessions[k]].name,
148- err
149- );
150- }
151- }
152- }
153- }
154-
155- impl PluginManager {
156- pub fn new() -> Self {
157- let socket_path = socket_path();
158- let _ = std::fs::remove_file(&socket_path);
159- let listener = UnixListener::bind(&socket_path).unwrap();
160- /*
161- debug!("bound");
162- // accept connections and process them, spawning a new thread for each one
163- thread::spawn(move || {
164- debug!("spawn");
165- let stream = listener.accept();
166- debug!("socket stream {:?}", &stream);
167- match stream {
168- Ok((mut stream, _)) => {
169- debug!("socket stream {:?}", &stream);
170- /* connection succeeded */
171- thread::spawn(move || {
172- debug!("socket listen {:?}", &stream);
173- debug!(initialize(stream));
174- //let mut response = Vec::new();
175- //debug!(stream.read_to_end(&mut response));
176- //loop {
177- // debug!("pre-flush 1");
178- // stream.flush();
179- // debug!("post-flush 1");
180- // if debug!(rmpv::decode::value::read_value(&mut stream)).is_err() {
181- // return;
182- // }
183- // debug!("post-read_value");
184- // //debug!("socket response {}", unsafe {
185- // // String::from_utf8_lossy(&response)
186- // //});
187- // stream.flush();
188- // debug!("post-flush 2");
189- // if debug!(rmpv::encode::write_value(
190- // &mut stream,
191- // &rmpv::Value::String("hello 2 u 2".into())
192- // ))
193- // .is_err()
194- // {
195- // return;
196- // }
197- // debug!("post-write_value");
198- //}
199- });
200- }
201- Err(err) => {
202- /* connection failed */
203- debug!(err);
204- }
205- }
206- });
207- */
208- let mut hooks: HashMap<String, UIHook> = Default::default();
209-
210- hooks.insert(
211- "attachment-view".to_string(),
212- UIHook {
213- name: "attachment-view".to_string(),
214- wait_response: true,
215- listeners: Vec::new(),
216- },
217- );
218-
219- hooks.insert(
220- "refresh-account".to_string(),
221- UIHook {
222- name: "refresh-account".to_string(),
223- wait_response: false,
224- listeners: Vec::new(),
225- },
226- );
227-
228- PluginManager {
229- plugins: Default::default(),
230- sessions: Default::default(),
231- instances: Default::default(),
232- streams: Default::default(),
233- hooks,
234- listener,
235- }
236- }
237-
238- pub fn register(&mut self, plugin: Plugin) -> Result<()> {
239- debug!(&plugin);
240- match plugin.kind {
241- PluginKind::LongLived => {
242- /* spawn thread */
243- let inv = &plugin.executable;
244- let child = std::process::Command::new("sh")
245- .args(&["-c", inv])
246- .stdin(Stdio::piped())
247- .stdout(Stdio::piped())
248- .spawn()?;
249- let (stream, _) = self.listener.accept()?;
250- /* send init message to plugin to register hooks */
251- let session = Uuid::new_v4();
252- let channel = RpcChannel::new(stream, &session)?;
253-
254- for h in &plugin.hooks {
255- self.add_listener(h, session);
256- }
257-
258- self.instances.insert(session, child);
259- self.sessions.insert(session, plugin.name.clone());
260- self.streams.insert(session, channel);
261- self.plugins.insert(plugin.name.clone(), plugin);
262- Ok(())
263- }
264- PluginKind::Filter => {
265- let session = Uuid::new_v4();
266- for h in &plugin.hooks {
267- self.add_listener(h, session);
268- }
269-
270- self.sessions.insert(session, plugin.name.clone());
271- self.plugins.insert(plugin.name.clone(), plugin);
272- /* send init message to plugin to register hooks */
273- Ok(())
274- }
275- PluginKind::Backend => {
276- self.plugins.insert(plugin.name.clone(), plugin);
277- /* send init message to plugin to register hooks */
278- Ok(())
279- }
280- }
281- }
282-
283- pub fn register_hook(&mut self, hook: UIHook) {
284- self.hooks.insert(hook.name.clone(), hook);
285- }
286-
287- pub fn add_listener(&mut self, hook: &str, session: Uuid) {
288- self.hooks
289- .entry(hook.to_string())
290- .and_modify(|entry| entry.listeners.push(session));
291- }
292-
293- pub fn activate_hook(&mut self, hook: &str, bytes: Vec<u8>) -> Result<FilterResult> {
294- debug!("activate_hook {}", hook);
295- debug!("bytes {:?}", &bytes);
296- for l in &self.hooks[hook].listeners {
297- let plugin = &self.plugins[&self.sessions[l]];
298- debug!(&plugin);
299- match &plugin.kind {
300- PluginKind::LongLived => {
301- debug!("listener: {}", l);
302- let channel = self.streams.get_mut(l).unwrap();
303- channel.write_ref(&rmpv::ValueRef::Binary(bytes.as_slice()))?;
304- let reply: Result<FilterResult> = channel.from_read();
305- return reply;
306- }
307- PluginKind::Filter => {
308- let inv = &plugin.executable;
309- let mut child = std::process::Command::new("sh")
310- .args(&["-c", inv])
311- .stdin(Stdio::piped())
312- .stdout(Stdio::piped())
313- .spawn()?;
314- let (stream, _) = self.listener.accept()?;
315- let mut channel = RpcChannel::new(stream, l)?;
316- channel.write_ref(&rmpv::ValueRef::Binary(bytes.as_slice()))?;
317- let reply: Result<FilterResult> = channel.from_read();
318- child.kill()?;
319- return reply;
320- }
321- k => {
322- debug!("got plugin kind {:?} in hook {}", k, hook);
323- }
324- }
325- }
326- Err(Error::new("no listeners for this hook"))
327- }
328-
329- pub fn listener(&self) -> UnixListener {
330- self.listener.try_clone().unwrap()
331- }
332- }
333-
334- #[derive(Debug)]
335- pub struct UIHook {
336- name: String,
337- wait_response: bool,
338- listeners: Vec<Uuid>,
339- }
340-
341- #[derive(Debug, Clone, Serialize, Deserialize)]
342- #[serde(rename_all = "snake_case")]
343- #[serde(tag = "t", content = "c")]
344- pub enum FilterResult {
345- UiMessage(String),
346- Text(String),
347- Ansi(String),
348- Binary(Vec<u8>),
349- Error(String),
350- }
351 diff --git a/meli/src/plugins/backend.rs b/meli/src/plugins/backend.rs
352deleted file mode 100644
353index 2891b16..0000000
354--- a/meli/src/plugins/backend.rs
355+++ /dev/null
356 @@ -1,303 +0,0 @@
357- /*
358- * meli - plugins
359- *
360- * Copyright 2019 Manos Pitsidianakis
361- *
362- * This file is part of meli.
363- *
364- * meli is free software: you can redistribute it and/or modify
365- * it under the terms of the GNU General Public License as published by
366- * the Free Software Foundation, either version 3 of the License, or
367- * (at your option) any later version.
368- *
369- * meli is distributed in the hope that it will be useful,
370- * but WITHOUT ANY WARRANTY; without even the implied warranty of
371- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
372- * GNU General Public License for more details.
373- *
374- * You should have received a copy of the GNU General Public License
375- * along with meli. If not, see <http://www.gnu.org/licenses/>.
376- */
377-
378- use super::*;
379- use melib::async_workers::{Async, AsyncBuilder, AsyncStatus, WorkContext};
380- use melib::backends::*;
381- use melib::conf::AccountSettings;
382- use melib::email::{Envelope, EnvelopeHash, Flag};
383- use melib::error::{Error, Result};
384- use std::collections::BTreeMap;
385- use std::collections::HashMap;
386- use std::sync::{Arc, Mutex, RwLock};
387-
388- // fields/interface/deserializing
389- #[derive(Debug, Clone, Serialize, Deserialize)]
390- struct SimpleEnvelope {
391- hash: EnvelopeHash,
392- subject: String,
393- from: String,
394- to: String,
395- date: String,
396- message_id: String,
397- references: String,
398- }
399-
400- #[derive(Debug)]
401- pub struct PluginBackend {
402- plugin: Plugin,
403- child: std::process::Child,
404- channel: Arc<Mutex<RpcChannel>>,
405- collection: melib::Collection,
406- is_online: Arc<Mutex<(std::time::Instant, Result<()>)>>,
407- }
408-
409- impl Drop for PluginBackend {
410- fn drop(&mut self) {
411- if let Err(err) = debug!(self.child.kill()) {
412- eprintln!(
413- "Error: could not kill process {} spawned by plugin {} ({})",
414- self.child.id(),
415- &self.plugin.name,
416- err
417- );
418- }
419- }
420- }
421-
422- impl MailBackend for PluginBackend {
423- fn capabilities(&self) -> MailBackendCapabilities {
424- const CAPABILITIES: MailBackendCapabilities = MailBackendCapabilities {
425- is_async: false,
426- is_remote: false,
427- supports_search: false,
428- extensions: None,
429- supports_tags: false,
430- supports_submission: false,
431- };
432- CAPABILITIES
433- }
434-
435- fn is_online(&self) -> Result<()> {
436- if let Ok(mut is_online) = self.is_online.try_lock() {
437- let now = std::time::Instant::now();
438- if now.duration_since(is_online.0) >= std::time::Duration::new(2, 0) {
439- if let Ok(mut channel) = self.channel.try_lock() {
440- channel.write_ref(&rmpv::ValueRef::Ext(BACKEND_FN, b"is_online"))?;
441- debug!(channel.expect_ack())?;
442- let ret: PluginResult<()> = debug!(channel.from_read())?;
443- is_online.0 = now;
444- is_online.1 = ret.into();
445- }
446- }
447- is_online.1.clone()
448- } else {
449- Err(Error::new("busy"))
450- }
451- }
452-
453- fn fetch(&mut self, _mailbox_hash: MailboxHash) -> Result<Async<Result<Vec<Envelope>>>> {
454- let mut w = AsyncBuilder::new();
455- let channel = self.channel.clone();
456- let handle = {
457- let tx = w.tx();
458- let closure = move |_work_context| {
459- let mut channel = channel.lock().unwrap();
460- channel
461- .write_ref(&rmpv::ValueRef::Ext(BACKEND_FN, b"get"))
462- .unwrap();
463- channel.expect_ack().unwrap();
464- loop {
465- let read_val: Result<PluginResult<Option<Vec<SimpleEnvelope>>>> =
466- channel.from_read();
467- match read_val.map(Into::into).and_then(std::convert::identity) {
468- Ok(Some(a)) => {
469- tx.send(AsyncStatus::Payload(Ok(a
470- .into_iter()
471- .filter_map(
472- |SimpleEnvelope {
473- hash,
474- date,
475- from,
476- to,
477- subject,
478- message_id,
479- references,
480- }| {
481- let mut env = melib::Envelope::new(hash);
482- env.set_date(date.as_bytes());
483- if let Ok(d) =
484- melib::email::parser::generic::date(date.as_bytes())
485- {
486- env.set_datetime(d);
487- }
488- env.set_message_id(message_id.as_bytes());
489- let parse_result =
490- melib::email::parser::address::rfc2822address_list(
491- from.as_bytes(),
492- );
493- if parse_result.is_ok() {
494- let value = parse_result.unwrap().1;
495- env.set_from(value);
496- }
497- let parse_result =
498- melib::email::parser::address::rfc2822address_list(
499- to.as_bytes(),
500- );
501- if parse_result.is_ok() {
502- let value = parse_result.unwrap().1;
503- env.set_to(value);
504- }
505- let parse_result = melib::email::parser::encodings::phrase(
506- subject.as_bytes(),
507- false,
508- );
509- if parse_result.is_ok() {
510- let value = parse_result.unwrap().1;
511- env.set_subject(value);
512- }
513- if !references.is_empty() {
514- env.set_references(references.as_bytes());
515- }
516-
517- Some(env)
518- },
519- )
520- .collect::<Vec<Envelope>>())))
521- .unwrap();
522- }
523- Ok(None) => {
524- tx.send(AsyncStatus::Finished).unwrap();
525- return;
526- }
527- Err(err) => {
528- tx.send(AsyncStatus::Payload(Err(err))).unwrap();
529- tx.send(AsyncStatus::Finished).unwrap();
530- return;
531- }
532- };
533- }
534- };
535- Box::new(closure)
536- };
537- Ok(w.build(handle))
538- }
539-
540- fn mailboxes(&self) -> ResultFuture<HashMap<MailboxHash, Mailbox>> {
541- let mut ret: HashMap<MailboxHash, Mailbox> = Default::default();
542- ret.insert(0, Mailbox::default());
543- Ok(Box::pin(async { Ok(ret) }))
544- }
545-
546- fn operation(&self, hash: EnvelopeHash) -> Result<Box<dyn BackendOp>> {
547- Ok(Box::new(PluginOp {
548- hash,
549- channel: self.channel.clone(),
550- bytes: None,
551- }))
552- }
553-
554- fn save(
555- &self,
556- _bytes: Vec<u8>,
557- _mailbox_hash: MailboxHash,
558- _flags: Option<Flag>,
559- ) -> ResultFuture<()> {
560- Err(Error::new("Saving is currently unimplemented for plugins"))
561- }
562- fn create_mailbox(
563- &mut self,
564- _name: String,
565- ) -> ResultFuture<(MailboxHash, HashMap<MailboxHash, Mailbox>)> {
566- Err(Error::new(
567- "Creating a mailbox is currently unimplemented for plugins",
568- ))
569- }
570- fn collection(&self) -> melib::Collection {
571- self.collection.clone()
572- }
573- fn as_any(&self) -> &dyn ::std::any::Any {
574- self
575- }
576- }
577-
578- impl PluginBackend {
579- pub fn new(
580- listener: UnixListener,
581- plugin: Plugin,
582- _s: &AccountSettings,
583- _is_subscribed: Box<dyn Fn(&str) -> bool>,
584- _ev: melib::backends::BackendEventConsumer,
585- ) -> Result<Box<dyn MailBackend>> {
586- if plugin.kind != PluginKind::Backend {
587- return Err(Error::new(format!(
588- "Error: Plugin `{}` is not a mail backend plugin, it's `{:?}`",
589- &plugin.name, &plugin.kind
590- )));
591- }
592- let inv = &plugin.executable;
593- let child = std::process::Command::new("sh")
594- .args(&["-c", inv])
595- .stdin(Stdio::piped())
596- .stdout(Stdio::piped())
597- .spawn()?;
598- let (stream, _) = listener.accept()?;
599- /* send init message to plugin to register hooks */
600- let session = Uuid::new_v4();
601- let channel = RpcChannel::new(stream, &session)?;
602- let now = std::time::Instant::now() - std::time::Duration::from_secs(5);
603-
604- Ok(Box::new(PluginBackend {
605- child,
606- plugin,
607- channel: Arc::new(Mutex::new(channel)),
608- collection: Default::default(),
609- is_online: Arc::new(Mutex::new((now, Err(Error::new("Uninitialized"))))),
610- }))
611- }
612-
613- pub fn register(listener: UnixListener, plugin: Plugin, backends: &mut Backends) {
614- backends.register(
615- plugin.name.clone(),
616- Backend {
617- create_fn: Box::new(move || {
618- let plugin = plugin.clone();
619- let listener = listener.try_clone().unwrap();
620- Box::new(move |f, i, ev| {
621- let plugin = plugin.clone();
622- let listener = listener.try_clone().unwrap();
623- PluginBackend::new(listener, plugin, f, i, ev)
624- })
625- }),
626- validate_conf_fn: Box::new(|_| Ok(())),
627- },
628- );
629- }
630- }
631-
632- #[derive(Debug)]
633- struct PluginOp {
634- hash: EnvelopeHash,
635- channel: Arc<Mutex<RpcChannel>>,
636- bytes: Option<String>,
637- }
638-
639- impl BackendOp for PluginOp {
640- fn as_bytes(&self) -> ResultFuture<Vec<u8>> {
641- let hash = self.hash;
642- let channel = self.channel.clone();
643- Ok(Box::pin(async move {
644- if let Ok(mut channel) = channel.try_lock() {
645- channel.write_ref(&rmpv::ValueRef::Ext(BACKEND_OP_FN, b"as_bytes"))?;
646- debug!(channel.expect_ack())?;
647- channel.write_ref(&rmpv::ValueRef::Integer(hash.into()))?;
648- debug!(channel.expect_ack())?;
649- let bytes: Result<PluginResult<String>> = channel.from_read();
650- Ok(bytes
651- .map(Into::into)
652- .and_then(std::convert::identity)?
653- .into_bytes())
654- } else {
655- Err(Error::new("busy"))
656- }
657- }))
658- }
659- }
660 diff --git a/meli/src/plugins/python3/ansi-plugin.py b/meli/src/plugins/python3/ansi-plugin.py
661deleted file mode 100755
662index 507cae7..0000000
663--- a/meli/src/plugins/python3/ansi-plugin.py
664+++ /dev/null
665 @@ -1,48 +0,0 @@
666- #! /usr/bin/env python3
667- """
668- meli - sample plugin
669-
670- Copyright 2019 Manos Pitsidianakis
671-
672- This file is part of meli.
673-
674- meli is free software: you can redistribute it and/or modify
675- it under the terms of the GNU General Public License as published by
676- the Free Software Foundation, either version 3 of the License, or
677- (at your option) any later version.
678-
679- meli is distributed in the hope that it will be useful,
680- but WITHOUT ANY WARRANTY; without even the implied warranty of
681- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
682- GNU General Public License for more details.
683-
684- You should have received a copy of the GNU General Public License
685- along with meli. If not, see <http://www.gnu.org/licenses/>.
686- """
687-
688- import sys
689- import subprocess
690- print(sys.path, file=sys.stderr)
691- from libmeliapi import Client
692-
693- if __name__ == "__main__":
694- server_address = './soworkfile'
695- client = Client(server_address)
696- client.connect()
697- try:
698- _bytes = client.read()
699- print('got bytes {!r}'.format(_bytes),file=sys.stderr, )
700-
701- # run() returns a CompletedProcess object if it was successful
702- # errors in the created process are raised here too
703- process = subprocess.run(['tiv','-w', '120','-h', '40', _bytes[0]], check=True, stdout=subprocess.PIPE, universal_newlines=True)
704- output = process.stdout
705- print('tiv output len {}'.format(len(output)),file=sys.stderr, )
706- #print('tiv output bytes {!r}'.format(output),file=sys.stderr, )
707-
708- message = { "t": "ansi", "c": output }
709- #print('sending {!r}'.format(message),file=sys.stderr, )
710- print('returned :', client.send(message), file=sys.stderr,)
711- except Exception as msg:
712- print(msg, file=sys.stderr,)
713-
714 diff --git a/meli/src/plugins/python3/libmeliapi.py b/meli/src/plugins/python3/libmeliapi.py
715deleted file mode 100644
716index 6475f79..0000000
717--- a/meli/src/plugins/python3/libmeliapi.py
718+++ /dev/null
719 @@ -1,179 +0,0 @@
720- """
721- meli - python3 api plugin
722-
723- Copyright 2019 Manos Pitsidianakis
724-
725- This file is part of meli.
726-
727- meli is free software: you can redistribute it and/or modify
728- it under the terms of the GNU General Public License as published by
729- the Free Software Foundation, either version 3 of the License, or
730- (at your option) any later version.
731-
732- meli is distributed in the hope that it will be useful,
733- but WITHOUT ANY WARRANTY; without even the implied warranty of
734- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
735- GNU General Public License for more details.
736-
737- You should have received a copy of the GNU General Public License
738- along with meli. If not, see <http://www.gnu.org/licenses/>.
739- """
740-
741- from collections import deque
742- import errno
743- import json
744- import msgpack
745- import socket
746- import struct
747- import sys
748- import time
749-
750- class IPCError(Exception):
751- pass
752-
753- class UnknownMessageClass(IPCError):
754- pass
755-
756- class InvalidSerialization(IPCError):
757- pass
758-
759- class ConnectionClosed(IPCError):
760- pass
761-
762-
763- def _read_objects(sock):
764- unpacker = msgpack.Unpacker()
765- ret = []
766- #reader = socket.socket.makefile(sock, 'rb')
767- while True:
768- try:
769- buf = sock.recv(1024**2)
770- if not buf:
771- break
772- unpacker.feed(buf)
773- for o in unpacker:
774- ret.append(o)
775- except:
776- break
777- return ret
778-
779- #try:
780- # for unpack in unpacker:
781- # return unpack
782- #except Exception as e:
783- # print("[libmeliapi]: ", "_read_objects error ", e, file=sys.stderr,)
784- # return None
785- #finally:
786- # reader.flush()
787-
788- def _write_objects(sock, objects):
789- sys.stderr.flush()
790- print("[libmeliapi]: ", "_write_objects ", objects, flush=True, file=sys.stderr, )
791- data = msgpack.packb(objects)
792- #print("[libmeliapi]: ", "_write_objects data ", data, flush=True, file=sys.stderr, )
793- sent = 0
794-
795- while sent < len(data):
796- try:
797- _len = min(len(data[sent:]), 2048)
798- sent += sock.send(data[sent:sent+_len])
799- except IOError as e:
800- print("[libmeliapi]: IOError: ", e, e.errno, flush=True, file=sys.stderr, )
801- sys.stderr.flush()
802- if e.errno == errno.EWOULDBLOCK:
803- break
804- elif e.errno == errno.EAGAIN:
805- time.sleep(0.001)
806- continue
807- else:
808- raise
809-
810- class Client(object):
811- def __init__(self, server_address):
812- self.buffer = deque()
813- self.addr = server_address
814- address_family = socket.AF_UNIX
815- self.sock = socket.socket(address_family, socket.SOCK_STREAM)
816- self.sock.setblocking(0)
817-
818- def connect(self):
819- try:
820- self.sock.connect(self.addr)
821-
822- print("[libmeliapi]: ", "self.send({ \"version\": \"dev\" }) = ",self.send({ "version": "dev" }), flush=True, file=sys.stderr)
823- self.expect_ack()
824- self._session = self.read()
825- self.ack()
826- print("[libmeliapi]: ", "self.buffer =", self.buffer, flush=True, file=sys.stderr, )
827- print("[libmeliapi]: ", "connected, session id is", self._session, flush=True, file=sys.stderr)
828- except socket.error as msg:
829- print("[libmeliapi]: ", msg, flush=True, file=sys.stderr, )
830- sys.stderr.flush()
831- sys.exit(1)
832-
833- def close(self):
834- self.sock.close()
835-
836- def setblocking(self, new_val):
837- self.sock.setblocking(new_val)
838-
839- def __enter__(self):
840- self.connect()
841- return self
842-
843- def __exit__(self, exc_type, exc_value, traceback):
844- self.close()
845-
846- def send(self, objects):
847- sys.stderr.flush()
848- #print("[libmeliapi]: ", "stuck in send ", self.buffer, flush=True, file=sys.stderr, )
849- _write_objects(self.sock, objects)
850- #print("[libmeliapi]: ", "unstuck wrote objs", flush=True, file=sys.stderr, )
851- #print("[libmeliapi]: ", "wrote object ", objects, file=sys.stderr)
852- time.sleep(0.001)
853-
854- def ack(self):
855- sys.stderr.flush()
856- _write_objects(self.sock, 0x06)
857- time.sleep(0.001)
858-
859- def expect_ack(self):
860- #print("[libmeliapi]: expect_ack, ", self.buffer, flush=True, file=sys.stderr, )
861- while True:
862- time.sleep(0.1)
863- read_list = _read_objects(self.sock)
864- self.buffer.extend(read_list)
865- try:
866- self.buffer.remove(0x6)
867- #print("[libmeliapi]: got_ack, ", self.buffer, flush=True, file=sys.stderr, )
868- return
869- except ValueError:
870- pass
871-
872- def read(self):
873- sys.stderr.flush()
874- #print("[libmeliapi]: ", "stuck in read ", self.buffer, flush=True, file=sys.stderr, )
875- read_list = _read_objects(self.sock)
876- time.sleep(0.01)
877- self.buffer.extend(read_list)
878- #print("[libmeliapi]: ", "unstuck read self.buffer =", self.buffer, flush=True, file=sys.stderr, )
879- if len(self.buffer) > 0:
880- return self.buffer.popleft()
881- else:
882- return None
883-
884- @property
885- def backend_fn_type(self):
886- return 0
887-
888- @property
889- def backend_op_fn_type(self):
890- return 1
891-
892- def ok_send(self, objects):
893- self.send({"t": "ok", "c": objects })
894- self.expect_ack()
895-
896- def err_send(self, objects):
897- self.send({"t": "err", "c": objects })
898- self.expect_ack()
899 diff --git a/meli/src/plugins/python3/nntp-backend.py b/meli/src/plugins/python3/nntp-backend.py
900deleted file mode 100755
901index e8a8c24..0000000
902--- a/meli/src/plugins/python3/nntp-backend.py
903+++ /dev/null
904 @@ -1,119 +0,0 @@
905- #! /usr/bin/env python3
906- """
907- meli - sample plugin
908-
909- Copyright 2019 Manos Pitsidianakis
910-
911- This file is part of meli.
912-
913- meli is free software: you can redistribute it and/or modify
914- it under the terms of the GNU General Public License as published by
915- the Free Software Foundation, either version 3 of the License, or
916- (at your option) any later version.
917-
918- meli is distributed in the hope that it will be useful,
919- but WITHOUT ANY WARRANTY; without even the implied warranty of
920- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
921- GNU General Public License for more details.
922-
923- You should have received a copy of the GNU General Public License
924- along with meli. If not, see <http://www.gnu.org/licenses/>.
925- """
926-
927- import sys
928- import time
929- import subprocess
930- import msgpack
931- import nntplib
932- import libmeliapi
933- import itertools
934-
935- def chunks(iterable, n):
936- while True:
937- try:
938- yield itertools.chain((next(iterable),), itertools.islice(iterable, n-1))
939- except:
940- break
941-
942- class NNTPClient(libmeliapi.Client):
943- def __init__(self, stream_address, server_address, newsgroup):
944- super().__init__(stream_address)
945- self.bytes_cache = {}
946- self.conn = nntplib.NNTP(server_address)
947- self.newsgroup = newsgroup
948- def backend_req(self, req):
949- print("[nntp-plugin]: backend_req = ", req, flush=True, file=sys.stderr)
950- if req.data == b'is_online':
951- self.ok_send(None)
952- elif req.data == b'get':
953- resp, count, first, last, name = self.conn.group(self.newsgroup)
954- print('Group', name, 'has', count, 'articles, range', first, 'to', last, flush=True, file=sys.stderr)
955-
956- resp, overviews = self.conn.over((0, last))
957- for chunk in chunks(iter(reversed(overviews)), 100):
958- ret = []
959- for id, over in chunk:
960- #print(id, nntplib.decode_header(over['subject']), flush=True, file=sys.stderr)
961- env = {}
962- env["hash"] = id
963- env["subject"] = nntplib.decode_header(over["subject"])
964- env["from"] = nntplib.decode_header(over["from"])
965- env["date"] = nntplib.decode_header(over["date"])
966- env["message_id"] = nntplib.decode_header(over["message-id"])
967- env["references"] = nntplib.decode_header(over["references"])
968- try:
969- env["to"] = nntplib.decode_header(over["to"])
970- except KeyError:
971- env["to"] = self.newsgroup
972- ret.append(env)
973- print("ret len = ", len(ret), flush=True,file=sys.stderr)
974- self.ok_send(ret)
975- self.ok_send(None)
976- def backend_op_req(self, req):
977- print("[nntp-plugin]: backend_op_req = ", req, flush=True, file=sys.stderr)
978- if req.data == b'as_bytes':
979- _hash = self.read()
980- print("[nntp-plugin]: hash = ", _hash, flush=True, file=sys.stderr)
981- self.ack()
982- try:
983- try:
984- self.ok_send(self.bytes_cache[_hash])
985- except KeyError:
986- resp, info = self.conn.article(_hash)
987- #print(_id, " line0 = ", str(info.lines[0], 'utf-8', 'ignore'))
988- elem = b'\n'.join(info.lines)
989- self.bytes_cache[_hash] = str(elem, 'utf-8', 'ignore')
990- self.ok_send(self.bytes_cache[_hash])
991- except Exception as e:
992- self.err_send(str(e))
993-
994-
995- if __name__ == "__main__":
996- import importlib
997- importlib.reload(libmeliapi)
998- stream_address = './soworkfile'
999- server_address = 'news.gmane.org'
1000- newsgroup = 'gmane.comp.python.committers'
1001- client = NNTPClient(stream_address, server_address, newsgroup)
1002- client.connect()
1003- #client.setblocking(True)
1004- try:
1005- while True:
1006- req = client.read()
1007- if req is None:
1008- time.sleep(0.15)
1009- continue
1010- #client.setblocking(True)
1011- client.ack()
1012- print("[nntp-plugin]: ", "req: ", req, flush=True, file=sys.stderr)
1013- sys.stderr.flush()
1014- if isinstance(req, msgpack.ExtType):
1015- if req.code == client.backend_fn_type:
1016- client.backend_req(req)
1017- elif req.code == client.backend_op_fn_type:
1018- client.backend_op_req(req)
1019- print("[nntp-plugin]: ", req, flush=True, file=sys.stderr)
1020- #client.setblocking(True)
1021- time.sleep(0.15)
1022- except:
1023- raise RuntimeError("Something bad happened")
1024 diff --git a/meli/src/plugins/rpc.rs b/meli/src/plugins/rpc.rs
1025deleted file mode 100644
1026index 88eaf06..0000000
1027--- a/meli/src/plugins/rpc.rs
1028+++ /dev/null
1029 @@ -1,142 +0,0 @@
1030- /*
1031- * meli - plugins
1032- *
1033- * Copyright 2019 Manos Pitsidianakis
1034- *
1035- * This file is part of meli.
1036- *
1037- * meli is free software: you can redistribute it and/or modify
1038- * it under the terms of the GNU General Public License as published by
1039- * the Free Software Foundation, either version 3 of the License, or
1040- * (at your option) any later version.
1041- *
1042- * meli is distributed in the hope that it will be useful,
1043- * but WITHOUT ANY WARRANTY; without even the implied warranty of
1044- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1045- * GNU General Public License for more details.
1046- *
1047- * You should have received a copy of the GNU General Public License
1048- * along with meli. If not, see <http://www.gnu.org/licenses/>.
1049- */
1050-
1051- use super::*;
1052-
1053- #[derive(Debug)]
1054- pub struct RpcChannel {
1055- stream: UnixStream,
1056- session: Uuid,
1057- }
1058-
1059- #[derive(Debug, Serialize, Deserialize)]
1060- #[serde(rename_all = "snake_case")]
1061- pub struct PluginGreeting {
1062- version: String,
1063- }
1064-
1065- impl RpcChannel {
1066- pub fn new(stream: UnixStream, session: &Uuid) -> Result<RpcChannel> {
1067- let mut ret = RpcChannel {
1068- stream,
1069- session: *session,
1070- };
1071- let greeting: PluginGreeting = ret
1072- .from_read()
1073- .map_err(|err| Error::new(format!("Could not get correct plugin greeting: {}", err)))?;
1074- debug!(&greeting);
1075- //if greeting.version != "dev" {
1076- // return Err("Plugin is not compatible with our API (dev)".into());
1077- //}
1078- ret.write_ref(&rmpv::ValueRef::String(session.to_string().as_str().into()))?;
1079- debug!(ret.expect_ack())?;
1080- Ok(ret)
1081- }
1082-
1083- pub fn expect_ack(&mut self) -> Result<()> {
1084- debug!("expect_ack()");
1085- let ack: u32 = debug!(rmp_serde::decode::from_read(&mut self.stream))
1086- .map_err(|_| Error::new("Plugin did not return ACK."))?;
1087- if 0x6 == ack {
1088- Ok(())
1089- } else {
1090- Err(Error::new("Plugin did not return ACK."))
1091- }
1092- }
1093-
1094- pub fn ack(&mut self) -> Result<()> {
1095- debug!("ack()");
1096- debug!(rmpv::encode::write_value_ref(
1097- &mut self.stream,
1098- &rmpv::ValueRef::Integer(0x6.into())
1099- ))
1100- .map_err(|err| Error::new(err.to_string()))?;
1101- let _ = self.stream.flush();
1102- Ok(())
1103- }
1104-
1105- pub fn write_ref(&mut self, value_ref: &rmpv::ValueRef) -> Result<()> {
1106- debug!("write_ref() {:?}", value_ref);
1107- debug!(rmpv::encode::write_value_ref(&mut self.stream, value_ref))
1108- .map_err(|err| Error::new(err.to_string()))?;
1109- let _ = self.stream.flush();
1110- Ok(())
1111- }
1112-
1113- pub fn read(&mut self) -> Result<rmpv::Value> {
1114- debug!("read()");
1115- let ret: RpcResult = debug!(rmp_serde::decode::from_read(&mut self.stream))
1116- .map_err(|err| Error::new(err.to_string()))?;
1117- let _ = self.stream.flush();
1118- self.ack()?;
1119- debug!("read() ret={:?}", &ret);
1120- ret.into()
1121- }
1122-
1123- pub fn from_read<T>(&mut self) -> Result<T>
1124- where
1125- T: std::fmt::Debug + serde::de::DeserializeOwned,
1126- {
1127- debug!("from_read()");
1128- let ret: Result<T> = debug!(rmp_serde::decode::from_read(&mut self.stream))
1129- .map_err(|err| Error::new(err.to_string()));
1130- let _ = self.stream.flush();
1131- self.ack()?;
1132- debug!("read() ret={:?}", &ret);
1133- ret
1134- }
1135- }
1136-
1137- #[derive(Debug, Clone, Serialize, Deserialize)]
1138- #[serde(rename_all = "snake_case")]
1139- #[serde(tag = "t", content = "c")]
1140- enum RpcResult {
1141- Ok(rmpv::Value),
1142- Err(String),
1143- }
1144-
1145- impl RpcResult {
1146- fn into(self) -> Result<rmpv::Value> {
1147- match self {
1148- RpcResult::Ok(v) => Ok(v),
1149- RpcResult::Err(err) => Err(Error::new(err)),
1150- }
1151- }
1152- }
1153-
1154- #[derive(Debug, Clone, Serialize, Deserialize)]
1155- #[serde(rename_all = "snake_case")]
1156- #[serde(tag = "t", content = "c")]
1157- pub enum PluginResult<T: std::fmt::Debug + Clone> {
1158- Ok(T),
1159- Err(String),
1160- }
1161-
1162- impl<T: std::fmt::Debug + Clone + serde::Serialize + serde::de::DeserializeOwned> Into<Result<T>>
1163- for PluginResult<T>
1164- {
1165- fn into(self) -> Result<T> {
1166- match self {
1167- PluginResult::Ok(v) => Ok(v),
1168- PluginResult::Err(err) => Err(Error::new(err)),
1169- }
1170- }
1171- }
1172 diff --git a/meli/src/state.rs b/meli/src/state.rs
1173index 2d228ce..817bee5 100644
1174--- a/meli/src/state.rs
1175+++ b/meli/src/state.rs
1176 @@ -343,10 +343,8 @@ impl State {
1177 sender: Sender<ThreadEvent>,
1178 receiver: Receiver<ThreadEvent>,
1179 ) -> Result<Self> {
1180- /*
1181- * Create async channel to block the input-thread if we need to fork and stop
1182- * it from reading stdin, see get_events() for details
1183- */
1184+ // Create async channel to block the input-thread if we need to fork and stop it
1185+ // from reading stdin, see get_events() for details
1186 let input_thread = unbounded();
1187 let input_thread_pipe = crate::types::pipe()?;
1188 let backends = Backends::new();
1189 @@ -355,20 +353,6 @@ impl State {
1190 } else {
1191 Settings::new()?
1192 });
1193- /*
1194- let mut plugin_manager = PluginManager::new();
1195- for (_, p) in settings.plugins.clone() {
1196- if crate::plugins::PluginKind::Backend == p.kind() {
1197- debug!("registering {:?}", &p);
1198- crate::plugins::backend::PluginBackend::register(
1199- plugin_manager.listener(),
1200- p.clone(),
1201- &mut backends,
1202- );
1203- }
1204- plugin_manager.register(p)?;
1205- }
1206- */
1207
1208 let (cols, rows) = termion::terminal_size().chain_err_summary(|| {
1209 "Could not determine terminal size. Are you running this on a tty? If yes, do you need \