[JIRA] (JENKINS-58249) Perforce plugin: JCasC cannot export configuration

69 views
Skip to first unread message

anton.bronnikov@gmail.com (JIRA)

unread,
Jun 28, 2019, 4:20:03 AM6/28/19
to jenkinsc...@googlegroups.com
Anton Bronnikov created an issue
 
Jenkins / Bug JENKINS-58249
Perforce plugin: JCasC cannot export configuration
Issue Type: Bug Bug
Assignee: Rob Petti
Components: perforce-plugin
Created: 2019-06-28 08:19
Environment: Jenkins 2.176.1
Perforce plugin 1.10.0
JCasC plugin 1.21
Labels: jcasc-compatibility
Priority: Major Major
Reporter: Anton Bronnikov

If P4 credentials are configured, then JCasC fails to export Jenkins configuration with an error:

 

{{credentials:
system:
domainCredentials:

  • credentials: "FAILED TO EXPORT com.cloudbees.plugins.credentials.domains.DomainCredentials#credentials:\
    \ java.lang.IllegalArgumentException: argument type mismatch at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native\
    \ Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)\
    \ at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)\
    \ at java.lang.reflect.Constructor.newInstance(Constructor.java:423) at\
    \ io.jenkins.plugins.casc.impl.configurators.DataBoundConfigurator.describe(DataBoundConfigurator.java:255)\
    \ at io.jenkins.plugins.casc.impl.configurators.HeteroDescribableConfigurator.lambda$convertToNode$de0cd4f8$1(HeteroDescribableConfigurator.java:224)\
    \ at io.vavr.CheckedFunction0.lambda$unchecked$52349c75$1(CheckedFunction0.java:201)\
    \ at io.jenkins.plugins.casc.impl.configurators.HeteroDescribableConfigurator.convertToNode(HeteroDescribableConfigurator.java:224)\
    \ at io.jenkins.plugins.casc.impl.configurators.HeteroDescribableConfigurator.lambda$describe$5(HeteroDescribableConfigurator.java:103)\
    \ at io.vavr.control.Option.map(Option.java:373) at io.jenkins.plugins.casc.impl.configurators.HeteroDescribableConfigurator.describe(HeteroDescribableConfigurator.java:103)\
    \ at io.jenkins.plugins.casc.impl.configurators.HeteroDescribableConfigurator.describe(HeteroDescribableConfigurator.java:51)\
    \ at io.jenkins.plugins.casc.Attribute.describe(Attribute.java:194) at io.jenkins.plugins.casc.impl.configurators.DataBoundConfigurator.describe(DataBoundConfigurator.java:264)\
    \ at io.jenkins.plugins.casc.Attribute.describe(Attribute.java:194) at com.cloudbees.plugins.credentials.casc.SystemCredentialsProviderConfigurator.describe(SystemCredentialsProviderConfigurator.java:76)\
    \ at com.cloudbees.plugins.credentials.casc.SystemCredentialsProviderConfigurator.describe(SystemCredentialsProviderConfigurator.java:48)\
    \ at io.jenkins.plugins.casc.Attribute.describe(Attribute.java:198) at com.cloudbees.plugins.credentials.casc.CredentialsRootConfigurator.describe(CredentialsRootConfigurator.java:90)\
    \ at com.cloudbees.plugins.credentials.casc.CredentialsRootConfigurator.describe(CredentialsRootConfigurator.java:52)"}}
Add Comment Add Comment
 
This message was sent by Atlassian Jira (v7.11.2#711002-sha1:fdc329d)

anton.bronnikov@gmail.com (JIRA)

unread,
Jun 28, 2019, 4:22:02 AM6/28/19
to jenkinsc...@googlegroups.com
Anton Bronnikov updated an issue
Change By: Anton Bronnikov
If P4 credentials are configured, then JCasC fails to export Jenkins configuration with an error:

 

{{credentials:
  system:
    domainCredentials:
    - credentials: "
FAILED TO EXPORT com.cloudbees.plugins.credentials.domains.DomainCredentials#credentials: \
        \
java.lang.IllegalArgumentException: argument type mismatch

anton.bronnikov@gmail.com (JIRA)

unread,
Jun 28, 2019, 4:23:03 AM6/28/19
to jenkinsc...@googlegroups.com
Anton Bronnikov updated an issue
If P4 credentials are configured, then JCasC fails to export Jenkins configuration with an error:

{ { quote}
FAILED TO EXPORT com.cloudbees.plugins.credentials.domains.DomainCredentials#credentials: java.lang.IllegalArgumentException: argument type mismatch }}
{{ at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) }}
{{
  
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) }}
{{ at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) }}
{{ at java.lang.reflect.Constructor.newInstance(Constructor.java:423) }}
{{
  
at io.jenkins.plugins.casc.impl.configurators.DataBoundConfigurator.describe(DataBoundConfigurator.java:255) }}
{{ at io.jenkins.plugins.casc.impl.configurators.HeteroDescribableConfigurator.lambda$convertToNode$de0cd4f8$1(HeteroDescribableConfigurator.java:224) }}
{{ at io.vavr.CheckedFunction0.lambda$unchecked$52349c75$1(CheckedFunction0.java:201) }}
{{ at io.jenkins.plugins.casc.impl.configurators.HeteroDescribableConfigurator.convertToNode(HeteroDescribableConfigurator.java:224) }}
{{ at io.jenkins.plugins.casc.impl.configurators.HeteroDescribableConfigurator.lambda$describe$5(HeteroDescribableConfigurator.java:103) }}
{{ at io.vavr.control.Option.map(Option.java:373) }}
{{
  
at io.jenkins.plugins.casc.impl.configurators.HeteroDescribableConfigurator.describe(HeteroDescribableConfigurator.java:103) }}
{{ at io.jenkins.plugins.casc.impl.configurators.HeteroDescribableConfigurator.describe(HeteroDescribableConfigurator.java:51) }}
{{ at io.jenkins.plugins.casc.Attribute.describe(Attribute.java:194) }}
{{
  
at io.jenkins.plugins.casc.impl.configurators.DataBoundConfigurator.describe(DataBoundConfigurator.java:264) }}
{{ at io.jenkins.plugins.casc.Attribute.describe(Attribute.java:194) }}
{{
  
at com.cloudbees.plugins.credentials.casc.SystemCredentialsProviderConfigurator.describe(SystemCredentialsProviderConfigurator.java:76) }}
{{ at com.cloudbees.plugins.credentials.casc.SystemCredentialsProviderConfigurator.describe(SystemCredentialsProviderConfigurator.java:48) }}
{{ at io.jenkins.plugins.casc.Attribute.describe(Attribute.java:198) }}
{{
  
at com.cloudbees.plugins.credentials.casc.CredentialsRootConfigurator.describe(CredentialsRootConfigurator.java:90) }}
{{ at com.cloudbees.plugins.credentials.casc.CredentialsRootConfigurator.describe(CredentialsRootConfigurator.java:52)

{quote
} }

anton.bronnikov@gmail.com (JIRA)

unread,
Jun 28, 2019, 4:23:03 AM6/28/19
to jenkinsc...@googlegroups.com
Anton Bronnikov updated an issue
If P4 credentials are configured, then JCasC fails to export Jenkins configuration with an error:

{{ FAILED TO EXPORT com.cloudbees.plugins.credentials.domains.DomainCredentials#credentials: java.lang.IllegalArgumentException: argument type mismatch }}
{{ at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
}}
{{
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) }}
{{ at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) }}
{{ at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
}}
{{
at io.jenkins.plugins.casc.impl.configurators.DataBoundConfigurator.describe(DataBoundConfigurator.java:255) }}
{{ at io.jenkins.plugins.casc.impl.configurators.HeteroDescribableConfigurator.lambda$convertToNode$de0cd4f8$1(HeteroDescribableConfigurator.java:224) }}
{{ at io.vavr.CheckedFunction0.lambda$unchecked$52349c75$1(CheckedFunction0.java:201) }}
{{ at io.jenkins.plugins.casc.impl.configurators.HeteroDescribableConfigurator.convertToNode(HeteroDescribableConfigurator.java:224) }}
{{ at io.jenkins.plugins.casc.impl.configurators.HeteroDescribableConfigurator.lambda$describe$5(HeteroDescribableConfigurator.java:103) }}
{{ at io.vavr.control.Option.map(Option.java:373)
}}
{{
at io.jenkins.plugins.casc.impl.configurators.HeteroDescribableConfigurator.describe(HeteroDescribableConfigurator.java:103) }}
{{ at io.jenkins.plugins.casc.impl.configurators.HeteroDescribableConfigurator.describe(HeteroDescribableConfigurator.java:51) }}
{{ at io.jenkins.plugins.casc.Attribute.describe(Attribute.java:194)
}}
{{
at io.jenkins.plugins.casc.impl.configurators.DataBoundConfigurator.describe(DataBoundConfigurator.java:264) }}
{{ at io.jenkins.plugins.casc.Attribute.describe(Attribute.java:194)
}}
{{
at com.cloudbees.plugins.credentials.casc.SystemCredentialsProviderConfigurator.describe(SystemCredentialsProviderConfigurator.java:76) }}
{{ at com.cloudbees.plugins.credentials.casc.SystemCredentialsProviderConfigurator.describe(SystemCredentialsProviderConfigurator.java:48) }}
{{ at io.jenkins.plugins.casc.Attribute.describe(Attribute.java:198)
}}
{{
at com.cloudbees.plugins.credentials.casc.CredentialsRootConfigurator.describe(CredentialsRootConfigurator.java:90) }}
{{ at com.cloudbees.plugins.credentials.casc.CredentialsRootConfigurator.describe(CredentialsRootConfigurator.java:52) }}

anton.bronnikov@gmail.com (JIRA)

unread,
Jun 28, 2019, 4:24:02 AM6/28/19
to jenkinsc...@googlegroups.com
Anton Bronnikov updated an issue
Change By: Anton Bronnikov
Component/s: p4-plugin
Component/s: perforce-plugin

kwirth@perforce.com (JIRA)

unread,
Jun 28, 2019, 6:09:02 AM6/28/19
to jenkinsc...@googlegroups.com
Karl Wirth assigned an issue to Karl Wirth
Change By: Karl Wirth
Assignee: Rob Petti Karl Wirth

kwirth@perforce.com (JIRA)

unread,
Jun 28, 2019, 6:21:02 AM6/28/19
to jenkinsc...@googlegroups.com
Karl Wirth assigned an issue to Unassigned
Change By: Karl Wirth
Assignee: Karl Wirth

pallen@perforce.com (JIRA)

unread,
Jun 28, 2019, 10:57:02 AM6/28/19
to jenkinsc...@googlegroups.com

pallen@perforce.com (JIRA)

unread,
Aug 16, 2019, 11:00:02 AM8/16/19
to jenkinsc...@googlegroups.com
Paul Allen updated an issue
Change By: Paul Allen
Labels: P4_A jcasc-compatibility

josephp90@gmail.com (JIRA)

unread,
Feb 4, 2020, 1:34:03 AM2/4/20
to jenkinsc...@googlegroups.com
Joseph Petersen commented on Bug JENKINS-58249
 
Re: Perforce plugin: JCasC cannot export configuration

Paul Allen please consider adding configuration-as-code tests and updating credentials plugin to v2.2.0 or higher.

Please read https://github.com/jenkinsci/configuration-as-code-plugin/blob/master/docs/PLUGINS.md

 

This message was sent by Atlassian Jira (v7.13.6#713006-sha1:cc4451f)
Atlassian logo

anton.bronnikov@gmail.com (JIRA)

unread,
Feb 5, 2020, 9:02:03 AM2/5/20
to jenkinsc...@googlegroups.com

No need to rush guys.  I happily use git/gitab combination already.

DavidRod777@gmail.com (JIRA)

unread,
Feb 5, 2020, 2:20:03 PM2/5/20
to jenkinsc...@googlegroups.com

I'm having a similar problem with P4 credentials configured in the jenkins.yaml file  No problem with gitlab credential.  When I add the P4 credential to the jenkins.yaml file,  II get an error when attempting to upload.  Possible the syntax for the p4 credential is incorrect.  Couldn't find an example

*Stack trace* io.jenkins.plugins.casc.ConfiguratorException: Invalid configuration elements for type class com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl : p4port.

Jenkins and plugin versions:

// 
 - Jenkins: v.2.204.2
 - Configuration as Code Plugin:  v1.35
 - Credentials Plugin:  v2.2.1
 - OS:  Ubuntu 18.04

*jenkins.yaml file:*  /var/jenkins_home/casc_configs/jenkins.yaml

The following shows how gitlab only is configured. Works fine.


credentials:
  system:
    domainCredentials:
    - credentials:
      - basicSSHUserPrivateKey:
          description: "Private key credentials for Jenkins GitLab user"
          id: "gitlab-key"
          privateKeySource:
            directEntry:
              privateKey: ${GITLAB_PRIVATE_KEY}
          scope: GLOBAL
          username: ${JENKINS_GITLAB_USER_NAME}
jenkins:
  agentProtocols:
  - "JNLP4-connect"
  - "Ping"
.
.

 
Added P4 password credentials to the jenkins.yaml file and attempted to upload but got the stack trace.   What is the correct syntax for the "Perforce Password Credential?   Is there an example I can use and try?

*Stack trace* io.jenkins.plugins.casc.ConfiguratorException: Invalid configuration elements for type class com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl : p4port.

// code placeholder
credentials:
  system:
    domainCredentials:
    - credentials:
      - basicSSHUserPrivateKey:
          description: "Private key credentials for Jenkins GitLab user"
          id: "gitlab-key"
          privateKeySource:
            directEntry:
              privateKey: ${GITLAB_PRIVATE_KEY}
          scope: GLOBAL
          username: ${JENKINS_GITLAB_USER_NAME}
      - usernamePassword:
          description: "Perforce password credential"
          id: "p4-userpass"
          p4port: "10.21.2.205:1667"
          scope: GLOBAL
          username: ${PERFORCE_USER_NAME}
          password: ${PERFORCE_PASSWORD}

.
.

 

 

 

 

tynril@gmail.com (JIRA)

unread,
Feb 5, 2020, 9:26:03 PM2/5/20
to jenkinsc...@googlegroups.com

Just encountered this issue as well.

Is there a valid way to express Perforce credentials in a Configuration-as-Code YAML file (even if it doesn't export cleanly)? Or is it not possible at the moment to define any Perforce credentials via Configuration-as-Code?

josephp90@gmail.com (JIRA)

unread,
Feb 6, 2020, 12:20:03 AM2/6/20
to jenkinsc...@googlegroups.com

It is currently not supported at all because of how p4 plugin is written.

Again it is not a problem with configuration-as-code-plugin but rather a problem with p4 plugin.

DavidRod777@gmail.com (JIRA)

unread,
Feb 6, 2020, 11:43:03 AM2/6/20
to jenkinsc...@googlegroups.com

Thanks Joseph,

Paul Allen:   This issue was created over 6 months ago.  Configuration-as-code is great solution on configuring Jenkins without the GUI and the ability to configure P4 credential is needed .  Are there plans to resolve this soon?  Otherwise have to find another solution to configure jenkins without the GUI.

DavidRod777@gmail.com (JIRA)

unread,
Feb 6, 2020, 11:44:05 AM2/6/20
to jenkinsc...@googlegroups.com
David Rodriguez edited a comment on Bug JENKINS-58249
Thanks Joseph,

@ Paul Allen:   This issue was created over 6 months ago.  Configuration-as-code is great solution on configuring Jenkins without the GUI and the ability to configure P4 credential is needed .  Are there plans to resolve this soon?  Otherwise have to find another solution to configure jenkins without the GUI.

tynril@gmail.com (JIRA)

unread,
Feb 7, 2020, 12:27:03 AM2/7/20
to jenkinsc...@googlegroups.com

After some investigation, I've found the root cause of this issue. I guess it's a partial responsibility between the configuration-as-code plugin, and the p4 plugin.

  • Additionally, in that same base class, a method named isSsl is defined. This method returns a boolean (and, importantly, not a TrustImpl object).
  • The method the configuration-as-code plugin uses to describe a data-bound type (as found in DataBoundConfigurator::describe) involves iterating over each argument of a class' constructor (which are assumed to be part of the description), and obtaining their current value on the given instance.
  • Obtaining said valid is done by calling the Attribute::locateGetter method (via Attribute::_getValue, passing it the class definition, and the name of the field to locate. locateGetter proceeds to iterate over all defined methods in the class, looking for a method taking no parameter named after the capitalized field name prefixed by either get or is.
  • In our case, the constructor parameter name is ssl, so locateGetter attempts to find a method named either getSsl or isSsl. Unfortunately for us, P4BaseCredential, the base class of our credential types, defines an isSsl method, as we've seen above. This is the method locateGetter returns, which is then used to obtain the value of our ssl field, which ends up being a boolean (since that's the return type of isSsl). This boolean, in DataBoundConfigurator::describe, gets added to the args array that is then used to construct a new instance of the class, by calling its data-bound constructor.
  • This is where the exception is raised: the type of the 5th element of args is boolean, but the 5th parameter of our constructors is of type TrustImpl. This throws an IllegalArgumentException which ends up being displayed as-is in the configuration export.

There are two fixes that I can see:

  • First, it seems odd that the configuration-as-code plugin would consider methods with an incompatible return type in Attribute::locateGetter. If that method was provided with the type of the field alongside its name, it could use that information to discard accessors whose name matches one of the two expected patterns, but whose type doesn't match the field. This would've caused locateGetter to return null, and _getValue would've then fallen back to locating a field matching the expected name (note that the comment there is misleading, forceAccess is true so it'll still find private fields), which would've been found. Note that in this execution path too, ExtraFieldUtils.GetField does not check for the field type at all – that might be worth looking at as well. In our specific case, though, the type would've been a match.
  • Second, the p4 plugin should probably consider using the convention around naming getters for data-bound constructors that the configuration-as-code plugin relies on. This would mean replacing the isSsl method with a getSsl one returning a TrustImpl. Note that just adding another valid getter without removing isSsl might or might not work, depending on the order the Java's reflection engine would place them: if isSsl is listed before getSsl is, without the aforementioned change to the configuration-as-code plugin, this bug would still happen.

josephp90@gmail.com (JIRA)

unread,
Feb 10, 2020, 1:03:04 AM2/10/20
to jenkinsc...@googlegroups.com

Samuel Loretan as JCasC again is opinionated and assumes that most plugin follows Jenkins conventions in data binding. Jenkins convention follows Java Bean conventions

Again I suggest any plugin developer reads https://github.com/jenkinsci/configuration-as-code-plugin/blob/master/docs/PLUGINS.md

josephp90@gmail.com (JIRA)

unread,
Feb 10, 2020, 1:04:05 AM2/10/20
to jenkinsc...@googlegroups.com
Joseph Petersen edited a comment on Bug JENKINS-58249
[~tynril] as JCasC again is opinionated and assumes that most plugin follows Jenkins conventions in data binding. Jenkins convention follows [Java Bean conventions|https://en.wikipedia.org/wiki/JavaBeans#JavaBean_conventions]

josephp90@gmail.com (JIRA)

unread,
Feb 10, 2020, 1:06:03 AM2/10/20
to jenkinsc...@googlegroups.com

However I am not saying JCasC is bug free. If you would like Samuel Loretan submit a PR that adds a tests and let's see if your suggested change would solve said test.

josephp90@gmail.com (JIRA)

unread,
Feb 10, 2020, 1:13:03 AM2/10/20
to jenkinsc...@googlegroups.com

After reading your post and looking at the code, I'd suggest the best fix is located in p4 plugin as it clearly does not follow Java Bean conventions.

pallen@perforce.com (JIRA)

unread,
Feb 17, 2020, 7:13:03 AM2/17/20
to jenkinsc...@googlegroups.com

Sorry for not looking at this sooner. As suggested I have added a getSsl() method into the Perforce Base Credentials.  I have manually tested this against the configuration-as-code plugin with a Perforce SSL connection and it looks good to me.

Please let me know if you are able to verify this fix and confirm there are no side effects when using SSL enabled Perforce Servers.

pallen@perforce.com (JIRA)

unread,
Feb 17, 2020, 8:46:02 AM2/17/20
to jenkinsc...@googlegroups.com
Paul Allen edited a comment on Bug JENKINS-58249
Sorry for not looking at this sooner. As suggested I have added a getSsl() method into the Perforce Base Credentials.  I have manually tested this against the configuration-as-code plugin with a Perforce SSL connection and it looks good to me.

Please let me know if you are able to verify this fix and confirm there are no side effects when using SSL enabled Perforce Servers.

DavidRod777@gmail.com (JIRA)

unread,
Mar 2, 2020, 3:49:02 PM3/2/20
to jenkinsc...@googlegroups.com

Hi Paul,

Would like to test this fix but I need to some help on how to configure the JCasC jenkins.yaml file for the P4 credential.   

Below  is my attempt to add P4 credential to the jenkins.yaml file  and restart jenkins.  Getting  stacktrace :

java.lang.IllegalArgumentException: No com.cloudbees.plugins.credentials.Credentials implementation found for P4BaseCredentials"

// code placeholder

credentials:
  system:
    domainCredentials:
    - credentials:
      - basicSSHUserPrivateKey:
          description: "Private key credentials for Jenkins GitLab user"
          id: "gitlab-key"
          privateKeySource:
            directEntry:
              privateKey: ${GITLAB_PRIVATE_KEY}
          scope: GLOBAL
          username: ${JENKINS_GITLAB_USER_NAME}
     - P4BaseCredentials:
          description: "Perforce password credential"
          id: "p4-userpass"
          p4port: "${PERFORCE_SERVER}:${PERFORCE_PORT}"
          scope: GLOBAL
          username: ${PERFORCE_USER_NAME}
          password: ${PERFORCE_PASSWORD}
 
                                                            

 

 

 

Add Comment Add Comment
 
This message was sent by Atlassian Jira (v7.13.12#713012-sha1:6e07c38)
Atlassian logo

tynril@gmail.com (JIRA)

unread,
Mar 2, 2020, 4:40:03 PM3/2/20
to jenkinsc...@googlegroups.com

David Rodriguez – not Paul, but P4BaseCredentials is an abstract base class, you probably want one of its concrete implementation instead (P4PasswordImpl or P4TicketImpl).

DavidRod777@gmail.com (JIRA)

unread,
Mar 3, 2020, 6:21:04 PM3/3/20
to jenkinsc...@googlegroups.com

Configured the JCasC jenkins.yaml file as follows with the "P4PasswordImpl" credentials  and was successful 

credentials:
  system:
    domainCredentials:
    - credentials:
      - basicSSHUserPrivateKey:
          description: "Private key credentials for Jenkins GitLab user"
          id: "gitlab-key"
          privateKeySource:
            directEntry:
              privateKey: "${GITLAB_PRIVATE_KEY}"
          scope: GLOBAL
          username: "${JENKINS_GITLAB_USER_NAME}"
      - P4PasswordImpl: 
          description: "Perforce password credential"
          id: "p4-userpass"
          p4port: "${P4PORT}"
          scope: GLOBAL
          username: "${P4USER}"
          password: "${P4PASSWD}"

When attempting to view the configuration as code configuration, got an export error by doing:

    Manage jenkins --> Configuration as Code --> Actions: View Configuration

credentials:
  system:
    domainCredentials:
    - credentials: |-
        FAILED TO EXPORT
        com.cloudbees.plugins.credentials.domains.DomainCredentials#credentials: java.lang.IllegalArgumentException: argument type mismatch
          at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
          at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)

Even though I got the EXPORT error, when trying to view the configuration,  the"P4 credential" in the jenkins.yaml  worked fine in a jenkins job.

 

Reply all
Reply to author
Forward
0 new messages