How to read multiline response from FTP server? - ftp

I am writing FTP client program in C. I am use blocking socket. I have put recv () in while loop and I break it when last two received characters are \r\n. I am able to run it on some servers but it doesn't read whole message on some servers like ftp.secyt.gov.ar.
I think the problem is because of the messages from server which contains characters \r \n together.
How should I deal with such case?
After sending the user name and password to server at ftp.secyt.gov.ar, I want to print the message received from server.
password = getpass("Password: ");
sprintf(pass, "PASS %s\r\n",password);
send(sockfd, pass, strlen(pass), 0);
while((no_of_bytes = recv(sockfd, message_from_server, MAXSZ, 0)) > 0)
{
message_from_server[no_of_bytes] = '\0';
printf("%s\n", message_from_server);
if (message_from_server[no_of_bytes-2] == '\r' &&
message_from_server[no_of_bytes-1] == '\n')
break;
}
Server sends this message:
230-=====================================================================
BIENVENIDOS AL SERVIDOR FTP DE LA MINCyT
----------------------------------------
Usuario anonymous, desde la maquina ::ffff:116.203.73.60, gracias por
utilizar el FTP del Ministerio de Ciencia, Tecnologia e
Innovacion Productiva.
Para sugerencias, consultas o informacin adicional nuestros correos
electrnicos son:
webmaster#mincyt.gov.ar
=========================================================================
230 User anonymous logged in.
But it is printing only:
230-=====================================================================

Your code reads a line (a string terminated by \r\n) from the server.
But an FTP response can be multi-line, according to the FTP specification.
See the RFC 959, section 4.2 FTP Replies:
Thus the format for multi-line replies is that the first line
will begin with the exact required reply code, followed
immediately by a Hyphen, "-" (also known as Minus), followed by
text. The last line will begin with the same code, followed
immediately by Space , optionally some text, and the Telnet
end-of-line code.
For example:
123-First line
Second line
234 A line beginning with numbers
123 The last line
The user-process then simply needs to search for the second
occurrence of the same reply code, followed by (Space), at
the beginning of a line, and ignore all intermediary lines. If
an intermediary line begins with a 3-digit number, the Server
must pad the front to avoid confusion.
See also How to know the end of FTP Welcome message.

Related

How to match EOF condition using antlr 4

I am new to ANTLR and I am currently writing a lexer for cool language in ANTLR 4.
For more about cool language please refer http://theory.stanford.edu/~aiken/software/cool/cool-manual.pdf.
One rule of cool language that I was trying to implement was detecting EOF inside Comments (may be nested) or String Constants and reporting as an error.
This is the rule that I wrote :
ERROR : '(*' (COMMENT|~['(*'|'*)'])*? (~['*)']) EOF {reportError("EOF in comment");}
|'"' (~[\n"])* EOF {reportError("EOF in string");};
fragment COMMENT : '(*' (COMMENT|~['(*'|'*)'])*? '*)'
Here the fragment COMMENT is a recursive rule that I used.
The function reportError used above reports error which is given below:
public void reportError(String errorString){
setText(errorString);
setType(ERROR);
}
But when I run it on the test file given below:
"Test String
It gives the following output :
line 1:0 token recognition error at: '"Test String\n'
#name "helloworld.cl"
Clearly the String with EOF in it was not recognised and ERROR was not detected.
Can someone help me in pointing out where I am going wrong as EOF (and hence, the error rule) is somehow not getting detected by the lexer.
If something is not clear please do mention it.
'"' (~[\n"])* EOF
Here the ~[\n"]* part will stop at the first \n or " or at the end of the file.
If it stops at a ", the rule does not match because the EOF does not match and that's what we want because the string literal is properly terminated.
If it stops at the end of file, then the subsequent EOF will match and you'll get an ERROR token. So that's also what you want.
But if it stops at a \n, the EOF will not match and you won't get an error token even though you'd want one in this case. And since your input ends with a \n, that's exactly the scenario you're running into here. So in addition to EOF, you should also allow for erroneous string literals to end in \n:
'"' (~[\n"])* ('\n' | EOF)
You don't need a dedicated ERROR rule. You can handle that specific situation with an unfinished string directly in your error listener. Your comment rule shouldn't be a fragment however, as it has to recognize a lexeme on its own that must be handled (fragment rules are rather rules to be used in other lexer rules only).
When the lexer reaches a string but cannot finish it due to the end of the input, you can get the offending input from the current lexer state in your error listener. You can then check that to see what exactly wasn't finished, like I do here for 3 quoted text types in MySQL:
void LexerErrorListener::syntaxError(Recognizer *recognizer, Token *, size_t line,
size_t charPositionInLine, const std::string &, std::exception_ptr ep) {
// The passed in string is the ANTLR generated error message which we want to improve here.
// The token reference is always null in a lexer error.
std::string message;
try {
std::rethrow_exception(ep);
} catch (LexerNoViableAltException &) {
Lexer *lexer = dynamic_cast<Lexer *>(recognizer);
CharStream *input = lexer->getInputStream();
std::string text = lexer->getErrorDisplay(input->getText(misc::Interval(lexer->tokenStartCharIndex, input->index())));
if (text.empty())
text = " "; // Should never happen.
switch (text[0]) {
case '/':
message = "Unfinished multiline comment";
break;
case '"':
message = "Unfinished double quoted string literal";
break;
case '\'':
message = "Unfinished single quoted string literal";
break;
case '`':
message = "Unfinished back tick quoted string literal";
break;
default:
// Hex or bin string?
if (text.size() > 1 && text[1] == '\'' && (text[0] == 'x' || text[0] == 'b')) {
message = std::string("Unfinished ") + (text[0] == 'x' ? "hex" : "binary") + " string literal";
break;
}
// Something else the lexer couldn't make sense of (likely there is no rule that accepts this input).
message = "\"" + text + "\" is no valid input at all";
break;
}
owner->addError(message, 0, lexer->tokenStartCharIndex, line, charPositionInLine,
input->index() - lexer->tokenStartCharIndex);
}
}
This code was taken from the parser module in MySQL Workbench.

Spring Integration - TCP Outbound Channel adds unwanted CRLF at the end of each line

I am reading large files using the following code and sending the file content over a TCP connection buffer by buffer. At the end of each send the TCP channel adds a CRLF character. I don't want this to appear in the results unless I add it.
final int BUFFER_SIZE = 65536;
long bytesToSkip = 0;
byte[] buffer = new byte[BUFFER_SIZE];
try (RandomAccessFile rand = new RandomAccessFile(new File(requestModel.getFilePath()), "r");
) {
rand.seek(bytesToSkip);
while ((read = rand.read(buffer)) != -1) {
MessageBuilder mb = MessageBuilder.withPayload(buffer).setHeaderIfAbsent(IpHeaders.CONNECTION_ID, connectionId);
outMsgChannel.send(mb.build())
buffer = new byte[BUFFER_SIZE];
}
}
catch(Exceptions ..............
Sample output where new line is added. (Both the buffers are huge. I have mentioned only the lines causing problems at the end of each buffer)
Buffer One contained
A quick brown fox jumps over the lazy dog
A quick brown fox jumps over
the lazy dog
A quick brown fox jumps over the
Buffer Two Contains
lazy dog
If there is no unwanted CRLF then I will not get the issue of single line splitting in to two in output.. I want to have new lines only where the file has.
See the documentation.
TCP is a streaming protocol; this means that some structure has to be provided to data transported over TCP, so the receiver can demarcate the data into discrete messages. Connection factories are configured to use (de)serializers to convert between the message payload and the bits that are sent over TCP. This is accomplished by providing a deserializer and serializer for inbound and outbound messages respectively. A number of standard (de)serializers are provided.
The ByteArrayCrlfSerializer, converts a byte array to a stream of bytes followed by carriage return and linefeed characters (\r\n). This is the default (de)serializer and can be used with telnet as a client, for example.
...
You need some way to know when the message is complete - the underlying network might packetize your message so it is received in chunks.
The ByteArrayRawSerializer adds no characters to the message; it might satisfy your needs. When used on the reading side, it uses the socket EOF to indicate the message is complete.

sed/awk insert line between 2 matches but only if there is not an other pattern

In order to write an script for an automated installation and configuration of Fail2Ban i need a way to modify a /etc/fail2ban/jail.local. My problem is, that i need to add a line in an specific section of the file, but only if the pattern enabled = true does not exist within the area. There are other areas where it shouldn’t write enable = true.
The following seds work fine, but do not check if there is already the enabled = true :
sed '/\[apache-auth\]/{x;s/.*/./;x};/port/{x;/^.$/{x;s/^/enabled = true\n/;x};s/^/./;x}' -i /etc/fail2ban/jail.local
sed '/\[apache-badbots\]/{x;s/.*/./;x};/port/{x;/^.$/{x;s/^/enabled = true\n/;x};s/^/./;x}' -i /etc/fail2ban/jail.local
sed '/\[apache-noscript\]/{x;s/.*/./;x};/port/{x;/^.$/{x;s/^/enabled = true\n/;x};s/^/./;x}' -i /etc/fail2ban/jail.local
sed '/^\[sshd\]/{x;s/.*/./;x};/port/{x;/^.$/{x;s/^/enabled = true\n/;x};s/^/./;x}' -i /etc/fail2ban/jail.local
Update adding requested information
original file
[sshd]
port = ssh
logpath = %(sshd_log)s
backend = %(sshd_backend)s
[apache-auth]
port = http,https
logpath = %(apache_error_log)s
[apache-badbots]
# Ban hosts which agent identifies spammer robots crawling the web
# for email addresses. The mail outputs are buffered.
port = http,https
logpath = %(apache_access_log)s
bantime = 172800
maxretry = 1
[apache-noscript]
port = http,https
logpath = %(apache_error_log)s
expected output
[sshd]
enabled = true
port = ssh
logpath = %(sshd_log)s
backend = %(sshd_backend)s
#
# HTTP servers
#
[apache-auth]
enabled = true
port = http,https
logpath = %(apache_error_log)s
[apache-badbots]
# Ban hosts which agent identifies spammer robots crawling the web
# for email addresses. The mail outputs are buffered.
enabled = true
port = http,https
logpath = %(apache_access_log)s
bantime = 172800
maxretry = 1
[apache-noscript]
enabled = true
port = http,https
logpath = %(apache_error_log)s
English is not my native, so please don't judge my spelling errors. I am also not sure, if this is the correct site for this Question.
This might work for you (GNU sed):
sed -nr '/^\[(apache-(auth|loadbots|noscript)|sshd)\]/{:a;x;/./!bb;/enabled = true/!s/^\w/enabled = true\n&/M;p;:b;x;h;d};H;$ba;d' file
This stores the section in the hold space, inserts enabled = true if it does not already exist and prints out the section.
In detail:
The command line switches -r and -n are activated, which allows regexps to be more easily expressed for the former and optional printing (grep-like i.e. the commands p or P must be present) for the latter.
The command consists of an if-then-else.
If the current line begins with [ and encloses one of four words, followed by ], the commands between { and } will be enacted.
The first command following the opening { is a place holder, used when the end-of-file condition is met.
The x means switch the pattern space (PS) with the hold space (HS). Sed has two registers, the PS is where the current line (minus its newline) read into. The HS is a spare register, to be used at the programmers discretion.
The next command /./!bb checks to see if the HS is empty (which will be if this is the first time this code has been executed) and if so jumps to the :b name space, missing out the following code.
Now that it is known that HS contains one or more lines, these lines are checked to see if they contain the string enabled = true and if not, the string enabled = true is inserted infront of the first line that begins with a word character.
Regardless of the match or non-match the HS is printed with p command.
The branch name space is now encounted :b and the HS and PS are exchanged, the current line replaces whatever is in the HS h and then deleted d, which ends the processing of that line.
If the first regexp failed: the current line is appended to the HS H and deleted d unless it was the last line in which case the processing is directed to the name space :a by the command $ba. This covers off the end-of-file condition in which lines in the HS will need to be processes as above.
awk to the rescue!
awk -v sections='sshd apache-auth apache-badbots apache-noscript' -v RS= -v ORS="\n\n" '
BEGIN {n=split(sections,a);
for(i=1; i<=n; i++) sec["["a[i]"]"]}
$1 in sec {$2 = "\nenabled = true\n"}1' file
maintains the tags in the sections variable, and inserts the required line for the corresponding sections. Expects the section headers to be the only entry on the line.

What are the rules that Vala's Process.spawn_command_line_async follows in interpreting CLI arguments?

Specifically, how does it interpret arguments that are in quotes or that feature redirects from standard input (e.g. <)?
I've got the following string:
string cmd = "mail -s 'Work Order #%s' -c %s -r email#server.com %s < email.txt".printf(wo.get_text(), ownmail, outmail.get_text());
When I use
Posix.system(cmd);
The command runs as expected and an email is sent, with the body taken from email.txt.
When I use
Process.spawn_command_line_async(cmd);
I get the error from the mail command that 'option -c is not found' or words to that effect. When I lose the quotes around Work Order #%s and instead escape the spaces, the email sends (with the subject line containing the back slashes) but instead of getting the body of the message from email.txt, it treats email.txt as another recipient of the email (it shows up in my inbox with 'email.txt' under the To: section). The < is being ignored or dropped. To check things out, I used
Process.spawn_command_line_async("echo %s".printf(cmd));
This showed me that the quotes around the subject line were being dropped but the < was still there. I can use Posix.system() in my program but for the sake of simplicity and reducing dependencies (and being more idiomatic), I'd prefer to use Process.spawn_command_line(). What am I missing?
Thank you!
You probably want to play around with Shell.quote() and Shell.unquote() in your "".printf() arguments.
The Vala Process.spawn_command_line_async() function is bound to GLib's g_spawn_command_line_async () function. So a good place to start looking for more details is the GLib documentation. The GLib documentation states g_spawn_command_line_async() uses g-shell-parse-argv to parse the command line. This parses the command line so the "results are defined to be the same as those you would get from a UNIX98 /bin/sh, as long as the input contains none of the unsupported shell expansions."
Also on that page are g_shell_quote () and g_shell_unquote (). These functions are bound to Vala as Shell.quote () and Shell.unquote ().
mail only accepts the body of the message from STDIN and g_spawn_command_line_async() won't handle the redirect. So you will either need a command line tool that takes the body as an argument or using something like Subprocess instead.
Thanks to both AIThomas and Jens sending me looking in the right direction, I was able to get it working with the following code:
static int main(string[] args) {
string subject = "-s " + Shell.quote("Work Order #123131");
string cc = "-c ccemail#org.org";
string frommail = "-r " + "senderemail#org.org";
string[] argv = {"mail", subject, cc, frommail, "destinationemail#org.org"};
int standard_input;
int child_pid;
Process.spawn_async_with_pipes (
".",
argv,
null,
SpawnFlags.SEARCH_PATH,
null,
out child_pid,
out standard_input,
null,
null);
FileStream instream = FileStream.fdopen(standard_input, "w");
instream.write("This is what will be emailed\n".data);
return 0;
}

ZPL command length limit?

I am generating ZPL command string and the printer (ZT410) does not print all the labels sent. I simply loop through the following code and send the string to the printer (via the IP address on port 9100). Each loop contains a unique set of data where row['item_desc'],row['our_part_number'], and part number change each iteration (so I am not just trying to print 522 identical labels).
command += '^XA';
command += '^LT23';
command += '^FO125,30';
command += '^A0N,85,50';
command += '^FB950,1,,C'
command += '^FD'+row['item_desc']+'^FS';
command += '^FO20,260';
command += '^A0N,50,50';
command += '^FD'+part_number+'^FS';
command += '^FO122,260';
command += '^A0N,50,50';
command += '^FB950,1,,C'
command += '^FD'+row['our_part_number']+'^FS';
command += '^FO935,255';
command += '^A0N,25,25';
command += '^FB200,2,,R'
command += '^FDMy Company Name Phone_Number ^FS';
command += '^FO'+margin+',105';
command += '^BY3';
command += '^B3N,N,144,N,N';
command += '^FD'+row['our_part_number']+'^FS';
command += '^XZ';
My test lot is 522 labels. Before adding the company info and barcode, the printer printed all 522 labels, but with the code above, it only prints 485 labels.
Where is the limit? Is there a limit on the string length that can be sent to the port? Should I add a carriage return and line feed after each label (so the printer knows where to break the string)? Or is there a IP:port timeout?
The printer prints the labels, but doesn't respond in any way to the port that sent the command. If I break the labels into individual command strings and send them sequentially without pause, the printer didn't print ANY labels. Is it possible to query the printer to know when to send the next command string?
Normally, I would just experiment with trial and error, but in this case that method is expensive and wasteful...
Try
^PQ522
as a command before the ^XZ.
I'd suggest you only send it once though. Sending it 522 times (or even 485 times) might just be a tad wasteful.
Use FN fields and save the label locally on the printer(using ^DF), then just call the label and populate the dynamic data in the FN fields

Resources