1. #!/usr/bin/perl
2. ######################################
3. ## Coded by Douglas[at]WeakNetLabs[dot]com
4. ## Coded by Chartreuse[at]WeakNetLabs[dot]com
5. #####################################
6. # TODO List: (Add to when new features are wanted with priorities 0-9 where 0 is urgent)
7. # + -7- Create configuration file code and store the environment there to be loaded
8. # + -6- Create a Help File/Help Page to display for command help
9. # + -3- Create more commands and populate them out
10. # + -9- Trap Arrow keys and implement functions to do scrollback
11. # + -5- Remove system call's in favor of modules -- system calls reduce portability
12. #
13. #
14. #
15. #
16. #
17. #####################################
18. # Change Log:
19. # [18/05/10 Chartreuse] Recoded Trevelyn's original code and added my own
20. # [19/05/10 Chartreuse] Added in Trevelyn's new code for mysql and such
21. # Made Help function print list of valid commands
22. # Moved parsing user input to it's own function
23. # Added support for command alias's
24. #
25. # [19/05/10 Trevelyn] Added Support for Negative Backreferencing in MySQL()
26. # function. Fixed token error for parsing /select/,
27. # /from/, /REGEXP/. Added quit(); Removed some
28. # print.
29. ##
30. use strict;
31. use warnings;
32.
33. #####################################
34. # Load up Modules
35. ##
36. use Cwd;
37. use Term::ANSIColor;
38. use File::Spec;
39. use POSIX;
40. use Term::Cap;
41.
42.
43. #####################################
44. # Pre-set Environment variables
45. ## TODO: Move to configuration file (.celeritasrc) and load on startup
46. my $hostname = "celeritas";
47. my $PS1 = colored (getlogin() . "\@$hostname", "bold green") . colored (" " . getWorkingDirectory() . "%", "bold blue") . " ";
48. my $version = "0.1.2ALPHA";
49. my @split_regexp;
50.
51. ####################################
52. # Global Variables -- Avoid Creating these
53. ## TODO: Find other solutions rather than these
54. my $term; # Needed for term::cap
55. my $termios = new POSIX::Termios; # Needed for term::cap
56. my $iAmAlive = 1; # Clean exit flag
57.
58. my $db = "";
59. my $usr = "";
60. my $col = 0;
61. my $pass = "";
62.
63. my @validCommands = qw /exit echo sys help clear mysql set use show quit/; # List of valid commands that can be symbolically called
64. my %commandAlias = ( # Hash of alias's, key = alias name, value = command name
65. "usedb" => "use"
66. );
67.
68. ####################################
69. ##### ENTRY POINT - Begin Here #####
70. ####################################
71. initialSetup();
72. mainLoop();
73. cleanUpAndQuit();
74. ###################################
75. ######### END ENTRY POINT #########
76. ###################################
77.
78.
79. ###################################
80. # Subroutines Here
81. ##
82. ### initialSetup() - Preconfigure everything before entering mainLoop()
83. # TODO: Unknown
84. sub initialSetup
85. {
86. ## Stuff needed for Term::Cap
87.
88. # General terminal line I/O
89. $termios->getattr;
90. # Extract the entry of the terminal type
91. $term = Term::Cap->Tgetent( { TERM => undef, OSPEED => $termios->getospeed } );
92.
93. return;
94. }
95. ### mainLoop() - Program Flow and Logic Here ###
96. # TODO: Unknown
97. sub mainLoop
98. {
99. my $returnValue=0;
100. while($iAmAlive) ## Logic Loop while we have not been told to exit nicely
101. {
102. printPrompt(); # Show us a prompt
103. parseUserInput();
104.
105. }
106.
107. return;
108. }
109. sub parseUserInput
110. {
111. chomp( my $userCommand = <STDIN> ); # Set $userCommand with user input and clean off any trailing newlines
112. $userCommand =~ /^(\S+)\s?(.*)/; # Return all characters up till first space and everything after first space
113.
114. my $commandName = $1;
115. my $commandArguments = $2;
116.
117. if(!( grep {$_ eq lc($1)} @validCommands))
118. {
119. if( exists $commandAlias{$1} ) # Check if we are dealing with a command alias
120. {
121. no strict 'refs'; # Should we even be doing the symbolic-references in the first place?
122.
123. return &{lc($commandAlias{$1} . "_cmd")}($2); # Does the postfix protection make this safe enough?
124. }
125. else
126. {
127. print colored("Celeritas:", "bold red") . colored(" $1","bold") . " command not understood\n";
128. return 0;
129. }
130. }
131. else
132. {
133. no strict 'refs'; # Should we even be doing the symbolic-references in the first place?
134. $commandName .= "_cmd"; # Append postfix to entered command and call it
135. return &{lc($commandName)}($2); # Does the postfix protection make this safe enough?
136. }
137. return 0;
138. }
139. ### printPrompt() - Prints the prompt ($PS1) to STDIN
140. # TODO: Unknown
141. sub printPrompt
142. {
143. print $PS1; # Just print out the prompt string for now
144. return;
145. }
146.
147. ### getWorkingDirectory() - Returns the current working directory without the full path
148. # TODO: Unknown
149. sub getWorkingDirectory
150. {
151. my @dirs = File::Spec->splitdir( Cwd::cwd() ); # Split the Current working path
152. return $dirs[$#dirs]; # Return the last element, (The current directory)
153. }
154.
155. ### cleanUpAndQuit() - Clean up all we've done, prepare to quit, and quit
156. # TODO: Unknown
157. sub cleanUpAndQuit
158. {
159. print "\n"; # Give shell some breathing room before leaving
160. exit(0);
161. }
162.
163. ####################################
164. # Celeritas Commands Below
165. ## TODO: Populate Commands
166.
167. ### exit_cmd - Command to nicely exit back to the shell
168. # TODO: Unknown
169. sub exit_cmd
170. {
171. print "Now Exiting... See you later :)\n";
172. $iAmAlive = 0;
173.
174. return;
175. }
176. sub quit_cmd
177. {
178. exit_cmd();
179. }
180. ### echo_cmd - Command to echo arguments out to STDOUT
181. # TODO: Possible Input Protection? Required?
182. sub echo_cmd
183. {
184. my ($input) = @_;
185. print "$input\n";
186.
187. return;
188. }
189. ### sys_cmd - Run command through the host shell
190. # TODO: Reimplement Trevelyn's Backref the REGEX code into a subroutine
191. sub sys_cmd
192. {
193. # Trevelyn's Backreference the REGEX Code
194. my ($input) = @_;
195. my @split;
196. if ($input =~ /\\-[0-9]/) {
197. @split = split(/\)/, $input);
198. foreach (@split) {
199. $_ =~ s/.*\(//g;
200. }
201. }
202. my $n = 0;
203. foreach (@split) {
204. $n++;
205. $input =~ s/\\-$n/\($_\)/g;
206. }
207.
208. system($input); # Run the command
209. return;
210. }
211. ### help_cmd - Display help pages to user
212. # TODO: Create Help Menu
213. sub help_cmd
214. {
215. print "Help\n";
216. print "-----------------\n";
217. print "Valid Commands: \n";
218. print join(" ", @validCommands) . "\n\n";
219.
220. return;
221. }
222. ### clear_cmd - Clears the screen
223. # TODO: Unknown
224. sub clear_cmd
225. {
226. print $term->Tputs('cl');
227. return;
228. }
229. ### mysql_cmd - Run mysql
230. # TODO: Use Modules instead of system command?
231. sub mysql_cmd {
232. my @split_cmd = split(" ", shift);
233. if (@split_cmd)
234. {
235. my ($what,$table,$regexp);
236. my $n = 0;
237. foreach (@split_cmd)
238. {
239. if ($_ =~ /select/i) { $what = $split_cmd[($n + 1)]; }
240. if ($_ =~ /from/i) { $table = $split_cmd[($n + 1)]; }
241. if ($_ =~ /REGEXP/) { $regexp = $split_cmd[($n + 1)]; }
242. $n++;
243. }
244. ### Addition For "neg backreferencing" function in MySQL!
245. # This is good, because the '\-[0-9]' will be allowed ANYWHERE else in the command!
246. # by simply changing ONLY the regexp text:
247. # TODO:
248. if ($regexp =~ /\\-[0-9]/) {
249. @split_regexp = split(/\)/, $regexp);
250. }
251. foreach (@split_regexp) {
252. $_ =~ s/.*\(//g;
253. }
254. $n = 0;
255. foreach (@split_regexp) {
256. $n++;
257. if ($_ !~ /\\-[0-9]/) {
258. $regexp =~ s/\\-$n/\($_\)/g;
259. }
260. }
261. # slurp all results line x line into array @lines:
262. ## SYSTEM COMMAND, perhaps use the MySQL module? ##
263. my @lines = `mysql -t -e \'select $what from $table\' $db --password=$pass --user=$usr`;
264. # get column of target for "where $something regexp..."
265.
266. my $g = 0;
267. print $lines[0] . $lines[1] . $lines[2];
268.
269. foreach( $lines[1] )
270. {
271. if ($_ =~ /where/i)
272. {
273. $col = ($g + 1);
274. last();
275. } $g++;
276. }
277.
278. my $t = $#lines;
279. foreach(@lines[3..$t])
280. {
281. my @split_lines = split(/" "/, $_);
282. # Here it is, if it's here, it gets printed:
283. if ($split_lines[$col] =~ /$regexp/)
284. {
285. chomp $_;
286. print "$_\n";
287. print $lines[0];
288. }
289. }
290. #print "The command was: select " . $what . " from " . " $table " . " where column " . $something . " had a regexp match of " . $regexp . "\n";
291. }
292. return;
293. }
294. ### set_cmd - Set variables for built in commands
295. # TODO: Add more? Maybe make these like environment variables
296. # and use a symbolic reference to store them as
297. # varname_env and make them accessable in commands
298. # and regex
299. sub set_cmd {
300. my @split_cmd = split(" ", shift);
301. if (@split_cmd)
302. {
303. if ($split_cmd[0] eq 'db' or $split_cmd[0] eq 'database') {
304. print "Database: ";
305. $db = <STDIN>;
306. chomp $db;
307. return;
308. }
309. if ($split_cmd[0] eq 'pass' or $split_cmd[0] eq 'passwd') {
310. print "Password: ";
311. system "stty -echo"; ## Use Term::Cap module to remove the need for system call?
312. $pass = <STDIN>;
313. chomp $pass;
314. system "stty echo"; ## Use Term::Cap module to remove the need for system call?
315. print "\n";
316. return;
317. }
318. if ($split_cmd[0] eq 'usr' or $split_cmd[0] eq 'user') {
319. print "Username: ";
320. $usr = <STDIN>;
321. chomp $usr;
322. return;
323. }else {
324. print "Error: set: " . $split_cmd[0] . " Not yet implemented.\n";
325. return;
326. }
327. }
328. return;
329. }
330. ### use_cmd - Set which database we are using
331. # Alias's: usedb
332. # TODO: Unknown
333. sub use_cmd {
334. my @split_cmd = split(" ", shift);
335. if (@split_cmd)
336. {
337. if ($split_cmd[0] eq '') {
338. print "Invalid name for database.\n";
339. return 0;
340. }
341. else {
342. $db = $split_cmd[0];
343. print "Database successfully changed to " . $db . "\n";
344. return;
345. }
346. }
347. }
348. ### show_cmd - Show all variables for built in commands
349. # TODO: Same as set_cmd, possibly use an env variables type setup
350. sub show_cmd {
351. my @split_cmd = split(" ", shift);
352. if (@split_cmd)
353. {
354. if ($split_cmd[0] eq 'db' or $split_cmd[0] eq 'database' or $split_cmd[0] eq 'dbase') {
355. print $db . "\n";
356. return;
357. }
358. if ($split_cmd[0] eq 'user' or $split_cmd[0] eq 'usr' or $split_cmd[0] eq 'who') {
359. print $usr . "\n";
360. return;
361. }
362. if ($split_cmd[0] eq 'version' or $split_cmd[0] eq 'v' or $split_cmd[0] eq 'V') {
363. print $version . "\n";
364. return;
365. }
366. if ($split_cmd[0] eq 'help') {
367. help();
368. }
369. else {
370. print "Error: " . $split_cmd[0] . " not yet implemented for command: \"show\"\n";
371. return;
372. }
373. }
374. }
375.